1 /*
2 * Tvheadend - Linux DVB EN50494
3 * (known under trademark "UniCable")
4 *
5 * Copyright (C) 2013 Sascha "InuSasha" Kuehndel
6 *
7 * This program is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 *
20 * Open things:
21 * - TODO: collision detection
22 * * compare transport-stream-id from stream with id in config
23 * * check continuity of the pcr-counter
24 * * when one point is given -> retry
25 * * delay time is easily random, but in standard is special (complicated) way described (cap. 8).
26 */
27
28 #include "tvheadend.h"
29 #include "linuxdvb_private.h"
30 #include "settings.h"
31
32 #include <unistd.h>
33 #include <math.h>
34
35 #include <linux/dvb/frontend.h>
36
37 /* **************************************************************************
38 * Static definition
39 * *************************************************************************/
40
41 #define LINUXDVB_EN50494_NOPIN 256
42
43 #define LINUXDVB_EN50494_FRAME 0xE0
44 /* addresses 0x00, 0x10 and 0x11 are possible */
45 #define LINUXDVB_EN50494_ADDRESS 0x10
46
47 #define LINUXDVB_EN50494_CMD_NORMAL 0x5A
48 #define LINUXDVB_EN50494_CMD_NORMAL_MULTIHOME 0x5C
49 /* special modes not implemented yet */
50 #define LINUXDVB_EN50494_CMD_SPECIAL 0x5B
51 #define LINUXDVB_EN50494_CMD_SPECIAL_MULTIHOME 0x5D
52
53 #define LINUXDVB_EN50494_SAT_A 0x00
54 #define LINUXDVB_EN50494_SAT_B 0x01
55
56 /* EN50607 */
57 #define LINUXDVB_EN50607_FRAME_NORMAL 0x70
58 #define LINUXDVB_EN50607_FRAME_MULTIHOME 0x71
59
60 /* **************************************************************************
61 * Class definition
62 * *************************************************************************/
63
64 /* prevention of self raised DiSEqC collisions */
65 static pthread_mutex_t linuxdvb_en50494_lock;
66
67 static const char *
linuxdvb_en50494_class_get_title(idnode_t * o,const char * lang)68 linuxdvb_en50494_class_get_title ( idnode_t *o, const char *lang )
69 {
70 static const char *title = N_("Unicable I (EN50494)");
71 return tvh_gettext_lang(lang, title);
72 }
73
74 static const char *
linuxdvb_en50607_class_get_title(idnode_t * o,const char * lang)75 linuxdvb_en50607_class_get_title ( idnode_t *o, const char *lang )
76 {
77 static const char *title = N_("Unicable II (EN50607)");
78 return tvh_gettext_lang(lang, title);
79 }
80
81 static htsmsg_t *
linuxdvb_en50494_position_list(void * o,const char * lang)82 linuxdvb_en50494_position_list ( void *o, const char *lang )
83 {
84 uint32_t i;
85 htsmsg_t *m = htsmsg_create_list();
86 for (i = 0; i < 2; i++) {
87 htsmsg_add_u32(m, NULL, i);
88 }
89 return m;
90 }
91
92 htsmsg_t *
linuxdvb_en50494_id_list(void * o,const char * lang)93 linuxdvb_en50494_id_list ( void *o, const char *lang )
94 {
95 uint32_t i;
96 htsmsg_t *m = htsmsg_create_list();
97 for (i = 0; i < 8; i++) {
98 htsmsg_add_u32(m, NULL, i);
99 }
100 return m;
101 }
102
103 htsmsg_t *
linuxdvb_en50607_id_list(void * o,const char * lang)104 linuxdvb_en50607_id_list ( void *o, const char *lang )
105 {
106 uint32_t i;
107 htsmsg_t *m = htsmsg_create_list();
108 for (i = 0; i < 32; i++) {
109 htsmsg_add_u32(m, NULL, i);
110 }
111 return m;
112 }
113
114 htsmsg_t *
linuxdvb_en50494_pin_list(void * o,const char * lang)115 linuxdvb_en50494_pin_list ( void *o, const char *lang )
116 {
117 int32_t i;
118
119 htsmsg_t *m = htsmsg_create_list();
120 htsmsg_t *e;
121
122 e = htsmsg_create_map();
123 htsmsg_add_u32(e, "key", 256);
124 htsmsg_add_str(e, "val", tvh_gettext_lang(lang, N_("No PIN")));
125 htsmsg_add_msg(m, NULL, e);
126
127 for (i = 0; i < 256; i++) {
128 e = htsmsg_create_map();
129 htsmsg_add_u32(e, "key", i);
130 htsmsg_add_u32(e, "val", i);
131 htsmsg_add_msg(m, NULL, e);
132 }
133 return m;
134 }
135
136 extern const idclass_t linuxdvb_diseqc_class;
137
138 const idclass_t linuxdvb_en50494_class =
139 {
140 .ic_super = &linuxdvb_diseqc_class,
141 .ic_class = "linuxdvb_en50494",
142 .ic_caption = N_("en50494"),
143 .ic_get_title = linuxdvb_en50494_class_get_title,
144 .ic_properties = (const property_t[]) {
145 {
146 .type = PT_U16,
147 .id = "position",
148 .name = N_("Position"),
149 .off = offsetof(linuxdvb_en50494_t, le_position),
150 .list = linuxdvb_en50494_position_list,
151 },
152 {
153 .type = PT_U16,
154 .id = "frequency",
155 .name = N_("Frequency"),
156 .off = offsetof(linuxdvb_en50494_t, le_frequency),
157 },
158 {
159 .type = PT_U16,
160 .id = "id",
161 .name = N_("SCR (ID)"),
162 .off = offsetof(linuxdvb_en50494_t, le_id),
163 .list = linuxdvb_en50494_id_list,
164 },
165 {
166 .type = PT_U16,
167 .id = "pin",
168 .name = N_("PIN"),
169 .off = offsetof(linuxdvb_en50494_t, le_pin),
170 .list = linuxdvb_en50494_pin_list,
171 },
172 {}
173 }
174 };
175
176 const idclass_t linuxdvb_en50607_class =
177 {
178 .ic_super = &linuxdvb_diseqc_class,
179 .ic_class = "linuxdvb_en50607",
180 .ic_caption = N_("en50607"),
181 .ic_get_title = linuxdvb_en50607_class_get_title,
182 .ic_properties = (const property_t[]) {
183 {
184 .type = PT_U16,
185 .id = "position",
186 .name = N_("Position"),
187 .off = offsetof(linuxdvb_en50494_t, le_position),
188 .list = linuxdvb_en50494_position_list,
189 },
190 {
191 .type = PT_U16,
192 .id = "frequency",
193 .name = N_("Frequency"),
194 .off = offsetof(linuxdvb_en50494_t, le_frequency),
195 },
196 {
197 .type = PT_U16,
198 .id = "id",
199 .name = N_("SCR (ID)"),
200 .off = offsetof(linuxdvb_en50494_t, le_id),
201 .list = linuxdvb_en50607_id_list,
202 },
203 {
204 .type = PT_U16,
205 .id = "pin",
206 .name = N_("PIN"),
207 .off = offsetof(linuxdvb_en50494_t, le_pin),
208 .list = linuxdvb_en50494_pin_list,
209 },
210 {}
211 }
212 };
213
214 /* **************************************************************************
215 * Class methods
216 * *************************************************************************/
217
218 static int
linuxdvb_en50494_freq0(linuxdvb_en50494_t * le,int freq,int * rfreq,uint16_t * t)219 linuxdvb_en50494_freq0
220 ( linuxdvb_en50494_t *le, int freq, int *rfreq, uint16_t *t )
221 {
222 /* transponder value - t */
223 *t = round((((freq / 1000) + 2 + le->le_frequency) / 4) - 350);
224 if (*t >= 1023) {
225 tvherror(LS_EN50494, "transponder value bigger then 1023 for freq %d (%d)", freq, le->le_frequency);
226 return -1;
227 }
228
229 /* tune frequency for the frontend */
230 *rfreq = (*t + 350) * 4000 - freq;
231 return 0;
232 }
233
234 static int
linuxdvb_en50607_freq0(linuxdvb_en50494_t * le,int freq,int * rfreq,uint16_t * t)235 linuxdvb_en50607_freq0
236 ( linuxdvb_en50494_t *le, int freq, int *rfreq, uint16_t *t )
237 {
238 /* transponder value - t */
239 *t = round((double)freq / 1000) - 100;
240 if (*t > 2047) {
241 tvherror(LS_EN50494, "transponder value bigger then 2047 for freq %d (%d)", freq, le->le_frequency);
242 return -1;
243 }
244
245 /* tune frequency for the frontend */
246 *rfreq = le->le_frequency * 1000;
247 return 0;
248 }
249
250 static int
linuxdvb_en50494_freq(linuxdvb_diseqc_t * ld,dvb_mux_t * lm,int freq)251 linuxdvb_en50494_freq
252 ( linuxdvb_diseqc_t *ld, dvb_mux_t *lm, int freq )
253 {
254 linuxdvb_en50494_t *le = (linuxdvb_en50494_t*) ld;
255 int rfreq;
256 uint16_t t;
257
258 if (linuxdvb_en50494_freq0(le, freq, &rfreq, &t))
259 return -1;
260 return rfreq;
261 }
262
263 static int
linuxdvb_en50494_match(linuxdvb_diseqc_t * ld,dvb_mux_t * lm1,dvb_mux_t * lm2)264 linuxdvb_en50494_match
265 ( linuxdvb_diseqc_t *ld, dvb_mux_t *lm1, dvb_mux_t *lm2 )
266 {
267 return lm1 == lm2;
268 }
269
270 static int
linuxdvb_en50494_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)271 linuxdvb_en50494_tune
272 ( linuxdvb_diseqc_t *ld, dvb_mux_t *lm,
273 linuxdvb_satconf_t *lsp, linuxdvb_satconf_ele_t *sc,
274 int vol, int pol, int band, int freq )
275 {
276 int ret = 0, i, fd = linuxdvb_satconf_fe_fd(lsp), rfreq;
277 int ver2 = linuxdvb_unicable_is_en50607(ld->ld_type);
278 linuxdvb_en50494_t *le = (linuxdvb_en50494_t*) ld;
279 uint8_t data1, data2, data3;
280 uint16_t t;
281
282
283 if (!ver2) {
284 /* tune frequency for the frontend */
285 if (linuxdvb_en50494_freq0(le, freq, &rfreq, &t))
286 return -1;
287 le->le_tune_freq = rfreq;
288 /* 2 data fields (16bit) */
289 data1 = (le->le_id & 7) << 5; /* 3bit user-band */
290 data1 |= (le->le_position & 1) << 4; /* 1bit position (satellite A(0)/B(1)) */
291 data1 |= (pol & 1) << 3; /* 1bit polarization v(0)/h(1) */
292 data1 |= (band & 1) << 2; /* 1bit band lower(0)/upper(1) */
293 data1 |= (t >> 8) & 3; /* 2bit transponder value bit 1-2 */
294 data2 = t & 0xff; /* 8bit transponder value bit 3-10 */
295 data3 = 0;
296 } else {
297 /* tune frequency for the frontend */
298 if (linuxdvb_en50607_freq0(le, freq, &rfreq, &t))
299 return -1;
300 le->le_tune_freq = rfreq;
301 /* 3 data fields (24bit) */
302 data1 = (le->le_id & 0x1f) << 3; /* 5bit user-band */
303 data1 |= (t >> 8) & 7; /* 3bit transponder value bit 1-3 */
304 data2 = t & 0xff; /* 8bit transponder value bit 4-11 */
305 data3 = (le->le_position & 0x3f) << 2; /* 6bit position */
306 data3 |= (pol & 1) << 1; /* 1bit polarization v(0)/h(1) */
307 data3 |= band & 1; /* 1bit band lower(0)/upper(1) */
308 }
309
310 /* wait until no other thread is setting up switch.
311 * when an other thread was blocking, waiting 20ms.
312 */
313 if (pthread_mutex_trylock(&linuxdvb_en50494_lock) != 0) {
314 if (pthread_mutex_lock(&linuxdvb_en50494_lock) != 0) {
315 tvherror(LS_EN50494,"failed to lock for tuning");
316 return -1;
317 }
318 tvh_safe_usleep(20000);
319 }
320
321 /* setup en50494 switch */
322 for (i = 0; i <= sc->lse_parent->ls_diseqc_repeats; i++) {
323 /* to avoid repeated collision, wait a random time 68-118
324 * 67,5 is the typical diseqc-time */
325 if (i != 0) {
326 uint8_t rnd;
327 uuid_random(&rnd, 1);
328 int ms = ((int)rnd)%50 + 68;
329 tvh_safe_usleep(ms*1000);
330 }
331
332 /* use 18V */
333 ret = linuxdvb_diseqc_set_volt(lsp, 1);
334 if (ret) {
335 tvherror(LS_EN50494, "error setting lnb voltage to 18V");
336 break;
337 }
338 tvh_safe_usleep(15000); /* standard: 4ms < x < 22ms */
339
340 /* send tune command (with/without pin) */
341 tvhdebug(LS_EN50494,
342 "lnb=%i id=%i freq=%i pin=%i v/h=%i l/u=%i f=%i, data=0x%02X%02X%02X",
343 le->le_position, le->le_id, le->le_frequency, le->le_pin, pol,
344 band, freq, data1, data2, data3);
345 if (!ver2 && le->le_pin != LINUXDVB_EN50494_NOPIN) {
346 ret = linuxdvb_diseqc_send(fd,
347 LINUXDVB_EN50494_FRAME,
348 LINUXDVB_EN50494_ADDRESS,
349 LINUXDVB_EN50494_CMD_NORMAL_MULTIHOME,
350 3,
351 data1, data2, (uint8_t)le->le_pin);
352 } else if (!ver2) {
353 ret = linuxdvb_diseqc_send(fd,
354 LINUXDVB_EN50494_FRAME,
355 LINUXDVB_EN50494_ADDRESS,
356 LINUXDVB_EN50494_CMD_NORMAL,
357 2,
358 data1, data2);
359 } else if (ver2 && le->le_pin != LINUXDVB_EN50494_NOPIN) {
360 ret = linuxdvb_diseqc_raw_send(fd, 5,
361 LINUXDVB_EN50607_FRAME_MULTIHOME,
362 data1, data2, data3, (uint8_t)le->le_pin);
363 } else if (ver2) {
364 ret = linuxdvb_diseqc_raw_send(fd, 4,
365 LINUXDVB_EN50607_FRAME_NORMAL,
366 data1, data2, data3);
367 }
368 if (ret != 0) {
369 tvherror(LS_EN50494, "error send tune command");
370 break;
371 }
372 tvh_safe_usleep(50000); /* standard: 2ms < x < 60ms */
373
374 /* return to 13V */
375 ret = linuxdvb_diseqc_set_volt(lsp, 0);
376 if (ret) {
377 tvherror(LS_EN50494, "error setting lnb voltage back to 13V");
378 break;
379 }
380 }
381 pthread_mutex_unlock(&linuxdvb_en50494_lock);
382
383 return ret == 0 ? 0 : -1;
384 }
385
386
387 /* **************************************************************************
388 * Create / Config
389 * *************************************************************************/
390
391 void
linuxdvb_en50494_init(void)392 linuxdvb_en50494_init (void)
393 {
394 if (pthread_mutex_init(&linuxdvb_en50494_lock, NULL) != 0) {
395 tvherror(LS_EN50494, "failed to init lock mutex");
396 }
397 }
398
399 htsmsg_t *
linuxdvb_en50494_list(void * o,const char * lang)400 linuxdvb_en50494_list ( void *o, const char *lang )
401 {
402 htsmsg_t *m = htsmsg_create_list();
403 htsmsg_add_msg(m, NULL, htsmsg_create_key_val("", tvh_gettext_lang(lang, N_("None"))));
404 htsmsg_add_msg(m, NULL, htsmsg_create_key_val(UNICABLE_I_NAME, tvh_gettext_lang(lang, N_(UNICABLE_I_NAME))));
405 htsmsg_add_msg(m, NULL, htsmsg_create_key_val(UNICABLE_II_NAME, tvh_gettext_lang(lang, N_(UNICABLE_II_NAME))));
406 return m;
407 }
408
409 linuxdvb_diseqc_t *
linuxdvb_en50494_create0(const char * name,htsmsg_t * conf,linuxdvb_satconf_ele_t * ls,int port)410 linuxdvb_en50494_create0
411 ( const char *name, htsmsg_t *conf, linuxdvb_satconf_ele_t *ls, int port )
412 {
413 linuxdvb_diseqc_t *ld;
414 linuxdvb_en50494_t *le;
415
416 if (strcmp(name ?: "", "Generic") &&
417 strcmp(name ?: "", UNICABLE_I_NAME) &&
418 strcmp(name ?: "", UNICABLE_II_NAME))
419 return NULL;
420
421 if (linuxdvb_unicable_is_en50494(name) && port > 1) {
422 tvherror(LS_EN50494, "only 2 ports/positions are possible. given %i", port);
423 port = 0;
424 }
425
426 if (linuxdvb_unicable_is_en50607(name) && port > 63) {
427 tvherror(LS_EN50494, "only 64 ports/positions are possible. given %i", port);
428 port = 0;
429 }
430
431 le = calloc(1, sizeof(linuxdvb_en50494_t));
432 if (le == NULL)
433 return NULL;
434 le->le_position = port;
435 le->le_id = 0;
436 le->le_frequency = 0;
437 le->le_pin = LINUXDVB_EN50494_NOPIN;
438 le->ld_freq = linuxdvb_en50494_freq;
439 le->ld_match = linuxdvb_en50494_match;
440
441 ld = linuxdvb_diseqc_create0((linuxdvb_diseqc_t *)le,
442 NULL,
443 linuxdvb_unicable_is_en50607(name) ?
444 &linuxdvb_en50607_class :
445 &linuxdvb_en50494_class,
446 conf, name, ls);
447 if (ld) {
448 ld->ld_tune = linuxdvb_en50494_tune;
449 /* May not needed: ld->ld_grace = linuxdvb_en50494_grace; */
450 }
451
452 return ld;
453 }
454
455 void
linuxdvb_en50494_destroy(linuxdvb_diseqc_t * le)456 linuxdvb_en50494_destroy ( linuxdvb_diseqc_t *le )
457 {
458 linuxdvb_diseqc_destroy(le);
459 free(le);
460 }
461