1 /*
2  *  Tvheadend - Linux DVB LNB config
3  *
4  *  Copyright (C) 2013 Adam Sutton
5  *
6  *  This program is free software: you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License as published by
8  *  the Free Software Foundation, either version 3 of the License, or
9  *  (at your option) any later version.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
18  */
19 
20 #include "tvheadend.h"
21 #include "linuxdvb_private.h"
22 #include "settings.h"
23 
24 #include <sys/ioctl.h>
25 #include <sys/types.h>
26 #include <sys/stat.h>
27 #include <unistd.h>
28 #include <fcntl.h>
29 #include <assert.h>
30 #include <linux/dvb/dmx.h>
31 
32 /* **************************************************************************
33  * Class definition
34  * *************************************************************************/
35 
36 typedef struct linuxdvb_lnb_conf
37 {
38   linuxdvb_lnb_t;
39 
40   /* Freq control */
41   int lnb_low;
42   int lnb_high;
43   int lnb_switch;
44 } linuxdvb_lnb_conf_t;
45 
46 static const char *
linuxdvb_lnb_class_get_title(idnode_t * o,const char * lang)47 linuxdvb_lnb_class_get_title ( idnode_t *o, const char *lang )
48 {
49   static char buf[256];
50   linuxdvb_diseqc_t *ld = (linuxdvb_diseqc_t*)o;
51   snprintf(buf, sizeof(buf), "LNB: %s", ld->ld_type);
52   return buf;
53 }
54 
55 extern const idclass_t linuxdvb_diseqc_class;
56 
57 const idclass_t linuxdvb_lnb_class =
58 {
59   .ic_super       = &linuxdvb_diseqc_class,
60   .ic_class       = "linuxdvb_lnb_basic",
61   .ic_caption     = N_("LNB"),
62   .ic_get_title   = linuxdvb_lnb_class_get_title,
63   .ic_properties = (const property_t[]) {
64     {
65       .type     = PT_INT,
66       .id       = "lfo",
67       .name     = N_("Low frequency offset"),
68       .opts     = PO_RDONLY | PO_NOSAVE,
69       .off      = offsetof(linuxdvb_lnb_conf_t, lnb_low),
70     },
71     {
72       .type     = PT_INT,
73       .id       = "hfo",
74       .name     = N_("High frequency offset"),
75       .opts     = PO_RDONLY | PO_NOSAVE,
76       .off      = offsetof(linuxdvb_lnb_conf_t, lnb_high),
77     },
78     {
79       .type     = PT_INT,
80       .id       = "sfo",
81       .name     = N_("Switch frequency offset"),
82       .opts     = PO_RDONLY | PO_NOSAVE,
83       .off      = offsetof(linuxdvb_lnb_conf_t, lnb_switch),
84     },
85     {}
86   }
87 };
88 
89 /* **************************************************************************
90  * Control functions
91  * *************************************************************************/
92 
93 /*
94  * Standard freq switched LNB
95  */
96 
97 static uint32_t
linuxdvb_lnb_standard_freq(linuxdvb_lnb_t * l,dvb_mux_t * lm)98 linuxdvb_lnb_standard_freq
99   ( linuxdvb_lnb_t *l, dvb_mux_t *lm )
100 {
101   linuxdvb_lnb_conf_t *lnb = (linuxdvb_lnb_conf_t*)l;
102   int32_t                f = (int32_t)lm->lm_tuning.dmc_fe_freq;
103   if (lnb->lnb_switch && f > lnb->lnb_switch)
104     f -= lnb->lnb_high;
105   else
106     f -= lnb->lnb_low;
107   return (uint32_t)abs(f);
108 }
109 
110 static int
linuxdvb_lnb_bandstack_match(linuxdvb_lnb_t * l,dvb_mux_t * lm1,dvb_mux_t * lm2)111 linuxdvb_lnb_bandstack_match
112   ( linuxdvb_lnb_t *l, dvb_mux_t *lm1, dvb_mux_t *lm2 )
113 {
114   /* everything is in one cable */
115   return 1;
116 }
117 
118 static int
linuxdvb_lnb_standard_match(linuxdvb_lnb_t * l,dvb_mux_t * lm1,dvb_mux_t * lm2)119 linuxdvb_lnb_standard_match
120   ( linuxdvb_lnb_t *l, dvb_mux_t *lm1, dvb_mux_t *lm2 )
121 {
122   linuxdvb_lnb_conf_t *lnb = (linuxdvb_lnb_conf_t*)l;
123   dvb_mux_conf_t      *dmc1 = &lm1->lm_tuning;
124   dvb_mux_conf_t      *dmc2 = &lm2->lm_tuning;
125 
126   if (lnb->lnb_switch) {
127     uint32_t hi1 = dmc1->dmc_fe_freq > lnb->lnb_switch;
128     uint32_t hi2 = dmc2->dmc_fe_freq > lnb->lnb_switch;
129     if (hi1 != hi2)
130       return 0;
131   }
132   if (dmc1->u.dmc_fe_qpsk.polarisation != dmc2->u.dmc_fe_qpsk.polarisation)
133     return 0;
134   return 1;
135 }
136 
137 static int
linuxdvb_lnb_standard_band(linuxdvb_lnb_t * l,dvb_mux_t * lm)138 linuxdvb_lnb_standard_band
139   ( linuxdvb_lnb_t *l, dvb_mux_t *lm )
140 {
141   linuxdvb_lnb_conf_t *lnb = (linuxdvb_lnb_conf_t*)l;
142   uint32_t               f = lm->lm_tuning.dmc_fe_freq;
143   return (lnb->lnb_switch && f > lnb->lnb_switch);
144 }
145 
146 static int
linuxdvb_lnb_standard_pol(linuxdvb_lnb_t * l,dvb_mux_t * lm)147 linuxdvb_lnb_standard_pol
148   ( linuxdvb_lnb_t *l, dvb_mux_t *lm )
149 {
150   dvb_mux_conf_t      *dmc = &lm->lm_tuning;
151   return dmc->u.dmc_fe_qpsk.polarisation == DVB_POLARISATION_HORIZONTAL ||
152          dmc->u.dmc_fe_qpsk.polarisation == DVB_POLARISATION_CIRCULAR_LEFT;
153 }
154 
155 static int
linuxdvb_lnb_inverted_pol(linuxdvb_lnb_t * l,dvb_mux_t * lm)156 linuxdvb_lnb_inverted_pol
157   ( linuxdvb_lnb_t *l, dvb_mux_t *lm )
158 {
159   return !linuxdvb_lnb_standard_pol(l, lm);
160 }
161 
162 static int
linuxdvb_lnb_standard_tune(linuxdvb_diseqc_t * ld,dvb_mux_t * lm,linuxdvb_satconf_t * lsp,linuxdvb_satconf_ele_t * ls,int vol,int pol,int band,int freq)163 linuxdvb_lnb_standard_tune
164   ( linuxdvb_diseqc_t *ld, dvb_mux_t *lm,
165     linuxdvb_satconf_t *lsp, linuxdvb_satconf_ele_t *ls,
166     int vol, int pol, int band, int freq )
167 {
168   return linuxdvb_diseqc_set_volt(ls->lse_parent, vol);
169 }
170 
171 /*
172  * Bandstacked polarity switch LNB
173  */
174 
175 static uint32_t
linuxdvb_lnb_bandstack_freq(linuxdvb_lnb_t * l,dvb_mux_t * lm)176 linuxdvb_lnb_bandstack_freq
177   ( linuxdvb_lnb_t *l, dvb_mux_t *lm )
178 {
179   linuxdvb_lnb_conf_t *lnb = (linuxdvb_lnb_conf_t*)l;
180   int32_t                f = (int32_t)lm->lm_tuning.dmc_fe_freq;
181   dvb_mux_conf_t      *dmc = &lm->lm_tuning;
182   int pol = dmc->u.dmc_fe_qpsk.polarisation == DVB_POLARISATION_HORIZONTAL ||
183             dmc->u.dmc_fe_qpsk.polarisation == DVB_POLARISATION_CIRCULAR_LEFT;
184   if (pol)
185     f -= lnb->lnb_high;
186   else
187     f -= lnb->lnb_low;
188   return (uint32_t)abs(f);
189 }
190 
191 static int
linuxdvb_lnb_bandstack_band(linuxdvb_lnb_t * l,dvb_mux_t * lm)192 linuxdvb_lnb_bandstack_band
193   ( linuxdvb_lnb_t *l, dvb_mux_t *lm )
194 {
195   dvb_mux_conf_t      *dmc = &lm->lm_tuning;
196   int pol = dmc->u.dmc_fe_qpsk.polarisation == DVB_POLARISATION_HORIZONTAL ||
197             dmc->u.dmc_fe_qpsk.polarisation == DVB_POLARISATION_CIRCULAR_LEFT;
198   return pol;
199 }
200 
201 static int
linuxdvb_lnb_bandstack_pol(linuxdvb_lnb_t * l,dvb_mux_t * lm)202 linuxdvb_lnb_bandstack_pol
203   ( linuxdvb_lnb_t *l, dvb_mux_t *lm )
204 {
205   return 0;
206 }
207 
208 /* **************************************************************************
209  * Static list
210  * *************************************************************************/
211 
212 linuxdvb_lnb_conf_t linuxdvb_lnb_all[] = {
213   {
214     { {
215       .ld_type    = "Universal",
216       .ld_tune    = linuxdvb_lnb_standard_tune,
217       },
218       .lnb_freq   = linuxdvb_lnb_standard_freq,
219       .lnb_match  = linuxdvb_lnb_standard_match,
220       .lnb_band   = linuxdvb_lnb_standard_band,
221       .lnb_pol    = linuxdvb_lnb_standard_pol,
222     },
223     .lnb_low    =  9750000,
224     .lnb_high   = 10600000,
225     .lnb_switch = 11700000,
226   },
227   {
228     { {
229       .ld_type    = "Standard",
230       .ld_tune    = linuxdvb_lnb_standard_tune,
231       },
232       .lnb_freq   = linuxdvb_lnb_standard_freq,
233       .lnb_match  = linuxdvb_lnb_standard_match,
234       .lnb_band   = linuxdvb_lnb_standard_band,
235       .lnb_pol    = linuxdvb_lnb_standard_pol,
236     },
237     .lnb_low    = 10000000,
238     .lnb_high   = 0,
239     .lnb_switch = 0,
240   },
241   {
242     { {
243       .ld_type    = "Enhanced",
244       .ld_tune    = linuxdvb_lnb_standard_tune,
245       },
246       .lnb_freq   = linuxdvb_lnb_standard_freq,
247       .lnb_match  = linuxdvb_lnb_standard_match,
248       .lnb_band   = linuxdvb_lnb_standard_band,
249       .lnb_pol    = linuxdvb_lnb_standard_pol,
250     },
251     .lnb_low    =  9750000,
252     .lnb_high   = 0,
253     .lnb_switch = 0,
254   },
255   {
256     { {
257       .ld_type    = "C-Band",
258       .ld_tune    = linuxdvb_lnb_standard_tune,
259       },
260       .lnb_freq   = linuxdvb_lnb_standard_freq,
261       .lnb_match  = linuxdvb_lnb_standard_match,
262       .lnb_band   = linuxdvb_lnb_standard_band,
263       .lnb_pol    = linuxdvb_lnb_standard_pol,
264     },
265     .lnb_low    =  5150000,
266     .lnb_high   = 0,
267     .lnb_switch = 0,
268   },
269   {
270     { {
271       .ld_type    = "C-Band (bandstack)",
272       .ld_tune    = linuxdvb_lnb_standard_tune,
273       },
274       .lnb_freq   = linuxdvb_lnb_bandstack_freq,
275       .lnb_match  = linuxdvb_lnb_bandstack_match,
276       .lnb_band   = linuxdvb_lnb_bandstack_band,
277       .lnb_pol    = linuxdvb_lnb_bandstack_pol,
278     },
279     .lnb_low    =  5150000,
280     .lnb_high   =  5750000,
281     .lnb_switch =  0,
282   },
283   {
284     { {
285       .ld_type    = "Ku 10750",
286       .ld_tune    = linuxdvb_lnb_standard_tune,
287       },
288       .lnb_freq   = linuxdvb_lnb_standard_freq,
289       .lnb_match  = linuxdvb_lnb_standard_match,
290       .lnb_band   = linuxdvb_lnb_standard_band,
291       .lnb_pol    = linuxdvb_lnb_standard_pol,
292     },
293     .lnb_low    = 10750000,
294     .lnb_high   = 0,
295     .lnb_switch = 0,
296   },
297   {
298     { {
299       .ld_type    = "Ku 10750 (Hi-Band)",
300       .ld_tune    = linuxdvb_lnb_standard_tune,
301       },
302       .lnb_freq   = linuxdvb_lnb_standard_freq,
303       .lnb_match  = linuxdvb_lnb_standard_match,
304       .lnb_band   = linuxdvb_lnb_standard_band,
305       .lnb_pol    = linuxdvb_lnb_standard_pol,
306     },
307     .lnb_low    = 10750000,
308     .lnb_high   = 10750000,
309     .lnb_switch = 10750000,
310   },
311   {
312     { {
313       .ld_type    = "Ku 10750 (Hi-Band, Inverted-Polar.)",
314       .ld_tune    = linuxdvb_lnb_standard_tune,
315       },
316       .lnb_freq   = linuxdvb_lnb_standard_freq,
317       .lnb_match  = linuxdvb_lnb_standard_match,
318       .lnb_band   = linuxdvb_lnb_standard_band,
319       .lnb_pol    = linuxdvb_lnb_inverted_pol,
320     },
321     .lnb_low    = 10750000,
322     .lnb_high   = 10750000,
323     .lnb_switch = 10750000,
324   },
325   {
326     { {
327       .ld_type    = "Ku 11300",
328       .ld_tune    = linuxdvb_lnb_standard_tune,
329       },
330       .lnb_freq   = linuxdvb_lnb_standard_freq,
331       .lnb_match  = linuxdvb_lnb_standard_match,
332       .lnb_band   = linuxdvb_lnb_standard_band,
333       .lnb_pol    = linuxdvb_lnb_standard_pol,
334     },
335     .lnb_low    = 11300000,
336     .lnb_high   = 0,
337     .lnb_switch = 0,
338   },
339   {
340     { {
341       .ld_type    = "Ku 11300 (Hi-Band)",
342       .ld_tune    = linuxdvb_lnb_standard_tune,
343       },
344       .lnb_freq   = linuxdvb_lnb_standard_freq,
345       .lnb_match  = linuxdvb_lnb_standard_match,
346       .lnb_band   = linuxdvb_lnb_standard_band,
347       .lnb_pol    = linuxdvb_lnb_standard_pol,
348     },
349     .lnb_low    = 11300000,
350     .lnb_high   = 11300000,
351     .lnb_switch = 11300000,
352   },
353   {
354      { {
355        .ld_type    = "C 5150/Ku 10750 (22kHz switch)",
356        .ld_tune    = linuxdvb_lnb_standard_tune,
357        },
358        .lnb_freq   = linuxdvb_lnb_standard_freq,
359        .lnb_band   = linuxdvb_lnb_standard_band,
360        .lnb_pol    = linuxdvb_lnb_standard_pol,
361      },
362      .lnb_low    =  5150000,
363      .lnb_high   = 10750000,
364      .lnb_switch = 11700000, /* 10750 + 950 (bottom of the receiver range 950Mhz-2150Mhz) */
365   },
366   {
367     { {
368       .ld_type    = "DBS",
369       .ld_tune    = linuxdvb_lnb_standard_tune,
370       },
371       .lnb_freq   = linuxdvb_lnb_standard_freq,
372       .lnb_match  = linuxdvb_lnb_standard_match,
373       .lnb_band   = linuxdvb_lnb_standard_band,
374       .lnb_pol    = linuxdvb_lnb_standard_pol,
375     },
376     .lnb_low    = 11250000,
377     .lnb_high   = 0,
378     .lnb_switch = 0,
379   },
380   {
381     { {
382       .ld_type    = "DBS Bandstack",
383       .ld_tune    = linuxdvb_lnb_standard_tune,
384       },
385       .lnb_freq   = linuxdvb_lnb_bandstack_freq,
386       .lnb_match  = linuxdvb_lnb_bandstack_match,
387       .lnb_band   = linuxdvb_lnb_bandstack_band,
388       .lnb_pol    = linuxdvb_lnb_bandstack_pol,
389     },
390     .lnb_low    = 11250000,
391     .lnb_high   = 14350000,
392     .lnb_switch = 0,
393   },
394   {
395     { {
396       .ld_type    = "Ku 10700 (Australia)",
397       .ld_tune    = linuxdvb_lnb_standard_tune,
398       },
399       .lnb_freq   = linuxdvb_lnb_standard_freq,
400       .lnb_match  = linuxdvb_lnb_standard_match,
401       .lnb_band   = linuxdvb_lnb_standard_band,
402       .lnb_pol    = linuxdvb_lnb_standard_pol,
403     },
404     .lnb_low    = 10700000,
405     .lnb_high   = 10700000,
406     .lnb_switch = 11800000,
407   },
408 };
409 
410 /* **************************************************************************
411  * Create / Config
412  * *************************************************************************/
413 
414 htsmsg_t *
linuxdvb_lnb_list(void * o,const char * lang)415 linuxdvb_lnb_list ( void *o, const char *lang )
416 {
417   int i;
418   htsmsg_t *m = htsmsg_create_list();
419   for (i = 0; i < ARRAY_SIZE(linuxdvb_lnb_all); i++)
420     htsmsg_add_str(m, NULL, linuxdvb_lnb_all[i].ld_type);
421   return m;
422 }
423 
424 linuxdvb_lnb_t *
linuxdvb_lnb_create0(const char * name,htsmsg_t * conf,linuxdvb_satconf_ele_t * ls)425 linuxdvb_lnb_create0
426   ( const char *name, htsmsg_t *conf, linuxdvb_satconf_ele_t *ls )
427 {
428   linuxdvb_lnb_conf_t *lsc = &linuxdvb_lnb_all[0], *lsc2;
429   int i;
430 
431   /* Find */
432   if (name) {
433     for (i = 0; i < ARRAY_SIZE(linuxdvb_lnb_all); i++)
434       if (!strcmp(linuxdvb_lnb_all[i].ld_type, name)) {
435         lsc = &linuxdvb_lnb_all[i];
436         break;
437       }
438   }
439 
440   lsc2 = malloc(sizeof(linuxdvb_lnb_conf_t));
441   *lsc2 = *lsc;
442   return (linuxdvb_lnb_t *)
443             linuxdvb_diseqc_create0((linuxdvb_diseqc_t *)lsc2,
444                                     NULL, &linuxdvb_lnb_class,
445                                     conf, lsc->ld_type, ls);
446 }
447 
448 void
linuxdvb_lnb_destroy(linuxdvb_lnb_t * lnb)449 linuxdvb_lnb_destroy ( linuxdvb_lnb_t *lnb )
450 {
451   linuxdvb_diseqc_destroy((linuxdvb_diseqc_t *)lnb);
452   free(lnb);
453 }
454 
455 /******************************************************************************
456  * Editor Configuration
457  *
458  * vim:sts=2:ts=2:sw=2:et
459  *****************************************************************************/
460