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