1 /*
2  *  Tvheadend - Linux DVB DiseqC switch
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 #include "hts_strtab.h"
24 
25 #include <sys/ioctl.h>
26 #include <sys/types.h>
27 #include <sys/stat.h>
28 #include <unistd.h>
29 #include <fcntl.h>
30 #include <assert.h>
31 #include <linux/dvb/dmx.h>
32 #include <linux/dvb/frontend.h>
33 
34 /* **************************************************************************
35  * Class definition
36  * *************************************************************************/
37 
38 typedef struct linuxdvb_switch
39 {
40   linuxdvb_diseqc_t;
41 
42   /* Port settings */
43   int ls_toneburst;
44   int ls_committed;
45   int ls_uncommitted;
46   int ls_uncommitted_first;
47   uint32_t ls_powerup_time; /* in ms */
48   uint32_t ls_sleep_time;   /* in ms */
49 
50 } linuxdvb_switch_t;
51 
52 static htsmsg_t *
linuxdvb_switch_class_committed_list(void * o,const char * lang)53 linuxdvb_switch_class_committed_list ( void *o, const char *lang )
54 {
55   static const struct strtab tab[] = {
56     { N_("NONE"), -1 },
57     { N_("AA"),    0 },
58     { N_("AB"),    1 },
59     { N_("BA"),    2 },
60     { N_("BB"),    3 },
61   };
62   return strtab2htsmsg(tab, 1, lang);
63 }
64 
65 static htsmsg_t *
linuxdvb_switch_class_uncommitted_list(void * o,const char * lang)66 linuxdvb_switch_class_uncommitted_list ( void *o, const char *lang )
67 {
68   static const struct strtab tab[] = {
69     { N_("NONE"), -1 },
70     { N_( "0"),    0 },
71     { N_( "1"),    1 },
72     { N_( "2"),    2 },
73     { N_( "3"),    3 },
74     { N_( "4"),    4 },
75     { N_( "5"),    5 },
76     { N_( "6"),    6 },
77     { N_( "7"),    7 },
78     { N_( "8"),    8 },
79     { N_( "9"),    9 },
80     { N_("10"),   10 },
81     { N_("11"),   11 },
82     { N_("12"),   12 },
83     { N_("13"),   13 },
84     { N_("14"),   14 },
85     { N_("15"),   15 },
86   };
87   return strtab2htsmsg(tab, 1, lang);
88 }
89 
90 static htsmsg_t *
linuxdvb_switch_class_toneburst_list(void * o,const char * lang)91 linuxdvb_switch_class_toneburst_list ( void *o, const char *lang )
92 {
93   static const struct strtab tab[] = {
94     { N_("NONE"), -1 },
95     { N_("A"),    0 },
96     { N_("B"),    1 },
97   };
98   return strtab2htsmsg(tab, 1, lang);
99 }
100 
101 static const char *
linuxdvb_switch_class_get_title(idnode_t * o,const char * lang)102 linuxdvb_switch_class_get_title ( idnode_t *o, const char *lang )
103 {
104   static char buf[256];
105   linuxdvb_diseqc_t *ld = (linuxdvb_diseqc_t*)o;
106   snprintf(buf, sizeof(buf), tvh_gettext_lang(lang, N_("Switch: %s")), ld->ld_type);
107   return buf;
108 }
109 
110 extern const idclass_t linuxdvb_diseqc_class;
111 
112 CLASS_DOC(linuxdvb_satconf)
113 
114 const idclass_t linuxdvb_switch_class =
115 {
116   .ic_super       = &linuxdvb_diseqc_class,
117   .ic_class       = "linuxdvb_switch",
118   .ic_caption     = N_("TV Adapters - SatConfig - DiseqC Switch"),
119   .ic_doc         = tvh_doc_linuxdvb_satconf_class,
120   .ic_get_title   = linuxdvb_switch_class_get_title,
121   .ic_properties  = (const property_t[]) {
122     {
123       .type    = PT_INT,
124       .id      = "committed",
125       .name    = N_("Committed"),
126       .off     = offsetof(linuxdvb_switch_t, ls_committed),
127       .list    = linuxdvb_switch_class_committed_list
128     },
129     {
130       .type    = PT_INT,
131       .id      = "uncommitted",
132       .name    = N_("Uncommitted"),
133       .off     = offsetof(linuxdvb_switch_t, ls_uncommitted),
134       .list    = linuxdvb_switch_class_uncommitted_list
135     },
136     {
137       .type    = PT_INT,
138       .id      = "toneburst",
139       .name    = N_("Tone burst"),
140       .off     = offsetof(linuxdvb_switch_t, ls_toneburst),
141       .list    = linuxdvb_switch_class_toneburst_list
142     },
143     {
144       .type    = PT_BOOL,
145       .id      = "preferun",
146       .name    = N_("Uncommitted first"),
147       .off     = offsetof(linuxdvb_switch_t, ls_uncommitted_first),
148     },
149     {
150       .type    = PT_U32,
151       .id      = "poweruptime",
152       .name    = N_("Power-up time (ms) (15-200)"),
153       .off     = offsetof(linuxdvb_switch_t, ls_powerup_time),
154       .def.u32 = 100,
155     },
156     {
157       .type    = PT_U32,
158       .id      = "sleeptime",
159       .name    = N_("Command delay time (ms) (10-200)"),
160       .off     = offsetof(linuxdvb_switch_t, ls_sleep_time),
161       .def.u32 = 25
162     },
163     {}
164   }
165 };
166 
167 /* **************************************************************************
168  * Class methods
169  * *************************************************************************/
170 
171 static int
linuxdvb_switch_tune(linuxdvb_diseqc_t * ld,dvb_mux_t * lm,linuxdvb_satconf_t * lsp,linuxdvb_satconf_ele_t * sc,int vol,int pol,int band,int freq)172 linuxdvb_switch_tune
173   ( linuxdvb_diseqc_t *ld, dvb_mux_t *lm, linuxdvb_satconf_t *lsp,
174     linuxdvb_satconf_ele_t *sc, int vol, int pol, int band, int freq )
175 {
176   int i, com, r1 = 0, r2 = 0, slp;
177   int fd = linuxdvb_satconf_fe_fd(lsp);
178   linuxdvb_switch_t *ls = (linuxdvb_switch_t*)ld;
179 
180   if (lsp->ls_diseqc_full || lsp->ls_last_switch != sc ||
181       (ls->ls_committed >= 0 &&
182         (pol + 1 != lsp->ls_last_switch_pol ||
183          band + 1 != lsp->ls_last_switch_band))) {
184 
185     lsp->ls_last_switch = NULL;
186 
187     if (linuxdvb_satconf_start(lsp, MINMAX(ls->ls_powerup_time, 15, 200), pol))
188       return -1;
189 
190     com = 0xF0 | (ls->ls_committed << 2) | (pol << 1) | band;
191     slp = MINMAX(ls->ls_sleep_time, 25, 200) * 1000;
192 
193     /* Repeats */
194     for (i = 0; i <= lsp->ls_diseqc_repeats; i++) {
195 
196       if (ls->ls_uncommitted_first)
197         /* Uncommitted */
198         if (ls->ls_uncommitted >= 0) {
199           if (linuxdvb_diseqc_send(fd, 0xE0 | r2, 0x10, 0x39, 1,
200                                    0xF0 | ls->ls_uncommitted))
201             return -1;
202           tvh_safe_usleep(slp);
203         }
204 
205       /* Committed */
206       if (ls->ls_committed >= 0) {
207         if (linuxdvb_diseqc_send(fd, 0xE0 | r1, 0x10, 0x38, 1, com))
208           return -1;
209         tvh_safe_usleep(slp);
210       }
211 
212       if (!ls->ls_uncommitted_first) {
213         /* Uncommitted */
214         if (ls->ls_uncommitted >= 0) {
215           if (linuxdvb_diseqc_send(fd, 0xE0 | r2, 0x10, 0x39, 1,
216                                    0xF0 | ls->ls_uncommitted))
217             return -1;
218           tvh_safe_usleep(slp);
219         }
220       }
221 
222       /* repeat flag */
223       r1 = r2 = 1;
224     }
225 
226     lsp->ls_last_switch      = sc;
227     lsp->ls_last_switch_pol  = pol + 1;
228     lsp->ls_last_switch_band = band + 1;
229 
230     /* port was changed, new LNB has not received toneburst yet */
231     lsp->ls_last_toneburst   = 0;
232   }
233 
234   /* Tone burst */
235   if (ls->ls_toneburst >= 0 &&
236       (lsp->ls_diseqc_full || lsp->ls_last_toneburst != ls->ls_toneburst + 1)) {
237 
238     if (linuxdvb_satconf_start(lsp, MINMAX(ls->ls_powerup_time, 15, 200), vol))
239       return -1;
240 
241     lsp->ls_last_toneburst = 0;
242     tvhtrace(LS_DISEQC, "toneburst %s", ls->ls_toneburst ? "B" : "A");
243     if (ioctl(fd, FE_DISEQC_SEND_BURST,
244               ls->ls_toneburst ? SEC_MINI_B : SEC_MINI_A)) {
245       tvherror(LS_DISEQC, "failed to set toneburst (e=%s)", strerror(errno));
246       return -1;
247     }
248     lsp->ls_last_toneburst = ls->ls_toneburst + 1;
249   }
250 
251   return 0;
252 }
253 
254 /* **************************************************************************
255  * Create / Config
256  * *************************************************************************/
257 
258 htsmsg_t *
linuxdvb_switch_list(void * o,const char * lang)259 linuxdvb_switch_list ( void *o, const char *lang )
260 {
261   htsmsg_t *m = htsmsg_create_list();
262   htsmsg_add_msg(m, NULL, htsmsg_create_key_val("", tvh_gettext_lang(lang, N_("None"))));
263   htsmsg_add_msg(m, NULL, htsmsg_create_key_val("Generic", tvh_gettext_lang(lang, N_("Generic"))));
264   return m;
265 }
266 
267 linuxdvb_diseqc_t *
linuxdvb_switch_create0(const char * name,htsmsg_t * conf,linuxdvb_satconf_ele_t * ls,int c,int u)268 linuxdvb_switch_create0
269   ( const char *name, htsmsg_t *conf, linuxdvb_satconf_ele_t *ls, int c, int u )
270 {
271   linuxdvb_switch_t *ld = NULL;
272   if (!strcmp(name ?: "", "Generic")) {
273     ld = (linuxdvb_switch_t*)linuxdvb_diseqc_create(linuxdvb_switch, NULL, conf, "Generic", ls);
274     if (ld) {
275       ld->ld_tune = linuxdvb_switch_tune;
276       if (!conf) {
277         ld->ls_committed     = -1;
278         ld->ls_uncommitted   = -1;
279         ld->ls_toneburst     = -1;
280         if (c >= 0) {
281           ld->ls_committed   = c;
282           ld->ls_toneburst   = c % 2;
283         }
284         if (u >= 0)
285           ld->ls_uncommitted = u;
286       }
287       if (ld->ls_powerup_time == 0)
288         ld->ls_powerup_time = 100;
289       if (ld->ls_sleep_time == 0)
290         ld->ls_sleep_time = 25;
291     }
292   }
293 
294   return (linuxdvb_diseqc_t*)ld;
295 }
296 
297 void
linuxdvb_switch_destroy(linuxdvb_diseqc_t * ld)298 linuxdvb_switch_destroy ( linuxdvb_diseqc_t *ld )
299 {
300   linuxdvb_diseqc_destroy(ld);
301   free(ld);
302 }
303 
304 /******************************************************************************
305  * Editor Configuration
306  *
307  * vim:sts=2:ts=2:sw=2:et
308  *****************************************************************************/
309