1 /*
2  * Copyright (C) 2014-2020 Beat Zahnd <beat.zahnd@gmail.com>
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
17  * USA
18  *
19  */
20 
21 #ifndef DISABLE_NETCVCLIENT
22 
23 #define _GNU_SOURCE
24 #include "netceiver.h"
25 #include <errno.h>
26 #include <fcntl.h>
27 #include <ifaddrs.h>
28 #include <string.h>
29 #include <sys/types.h>
30 #include <unistd.h>
31 
32 #define DEFAULT_LOG LOG_NETCEIVER
33 
34 SNetceiver *sn[MAX_ADAPTERS];
35 #define SN sn[ad->id]
36 
37 int handle_ts(unsigned char *buffer, size_t len, void *p);
38 int handle_ten(tra_t *ten, void *p);
39 
40 extern char *fe_pilot[];
41 extern char *fe_rolloff[];
42 extern char *fe_delsys[];
43 extern char *fe_fec[];
44 extern char *fe_modulation[];
45 extern char *fe_tmode[];
46 extern char *fe_gi[];
47 extern char *fe_hierarchy[];
48 extern char *fe_pol[];
49 
netcv_close(adapter * ad)50 int netcv_close(adapter *ad) {
51     SN->want_commit = 0;
52 
53     if (!SN->ncv_rec)
54         LOG("netceiver: receiver instance is NULL (id=%d)", ad->id);
55 
56     LOG("netceiver: delete receiver instance for adapter %d", ad->id);
57 
58     /* unregister handlers */
59     register_ten_handler(SN->ncv_rec, NULL, NULL);
60     register_ts_handler(SN->ncv_rec, NULL, NULL);
61 
62     /* call recv_init of libmcli to clean up the NetCeiver API */
63     recv_del(SN->ncv_rec);
64     SN->ncv_rec = NULL;
65 
66     /* close DVR pipe */
67     close(SN->pwfd);
68     close(ad->dvr);
69 
70     ad->strength = 0;
71     ad->status = 0;
72     ad->snr = 0;
73     ad->ber = 0;
74 
75     return 0;
76 }
77 
netcv_open_device(adapter * ad)78 int netcv_open_device(adapter *ad) {
79     SN->want_commit = 0;
80     SN->ncv_rec = NULL;
81 
82     /* create DVR pipe for TS data transfer from libmcli to minisatip */
83     int pipe_fd[2];
84     if (pipe2(pipe_fd, O_NONBLOCK))
85         LOG("netceiver: creating pipe failed (%s)", strerror(errno));
86     if (-1 == fcntl(pipe_fd[0], F_SETPIPE_SZ, 5 * 188 * 1024))
87         LOG("netceiver pipe buffer size change failed (%s)", strerror(errno));
88     ad->dvr = pipe_fd[0];  // read end of pipe
89     SN->pwfd = pipe_fd[1]; // write end of pipe
90     LOG("netceiver: creating DVR pipe for adapter %d  -> dvr: %d", ad->id,
91         ad->dvr);
92 
93     return 0;
94 }
95 
netcv_set_pid(adapter * ad,int pid)96 int netcv_set_pid(adapter *ad, int pid) {
97     int aid = ad->id;
98     LOG("netceiver: set_pid for adapter %d, pid %d, err %d", aid, pid, SN->err);
99 
100     if (SN->err) // error reported, return error
101         return 0;
102 
103     SN->npid[SN->lp] = pid;
104     SN->lp++;
105     SN->want_commit = 1;
106 
107     return aid + 100;
108 }
109 
netcv_del_pid(adapter * ad,int fd,int pid)110 int netcv_del_pid(adapter *ad, int fd, int pid) {
111     int i, hit = 0;
112     fd -= 100;
113     LOG("netceiver: del_pid for aid %d, pid %d, err %d", fd, pid, SN->err);
114     if (SN->err) // error reported, return error
115         return 0;
116 
117     for (i = 0; i < MAX_PIDS - 1; i++)
118         if (SN->npid[i] == pid || hit) {
119             SN->npid[i] = SN->npid[i + 1];
120             hit = 1;
121         }
122     if (hit && sn[fd]->lp > 0)
123         sn[fd]->lp--;
124     SN->want_commit = 1;
125 
126     return 0;
127 }
128 
netcv_commit(adapter * ad)129 int netcv_commit(adapter *ad) {
130     int i;
131 
132     int m_pos = 0;
133     fe_type_t type = 0;
134     recv_sec_t m_sec;
135     struct dvb_frontend_parameters m_fep;
136     dvb_pid_t m_pids[MAX_PIDS];
137 
138     /* check if we have to create a receiver instance first */
139     SN->err = 0;
140     if (!SN->ncv_rec) {
141         LOG("netceiver: add a new receiver instance for adapter %d", ad->id);
142 
143         /* call recv_add of libmcli to add a new receiver instance */
144         SN->ncv_rec = recv_add();
145 
146         /* register handlers */
147         register_ten_handler(SN->ncv_rec, &handle_ten, ad);
148         register_ts_handler(SN->ncv_rec, &handle_ts, SN);
149 
150         if (!SN->ncv_rec)
151             SN->err = 1;
152 
153         SN->want_tune = 0; // wait until netcv_tune triggers the tuning
154     }
155 
156     /* tune receiver to a new frequency / tranponder */
157     if (SN->want_tune) {
158         transponder *tp = &ad->tp;
159 
160         int map_pos[] = {0, 192, 130, 282,
161                          -50}; // Default sat positions: 19.2E, 13E, 28.2E, 5W
162         int map_pol[] = {0, SEC_VOLTAGE_13, SEC_VOLTAGE_18, SEC_VOLTAGE_OFF};
163 
164         switch (tp->sys) {
165         case SYS_DVBS:
166         case SYS_DVBS2:
167             m_pos = 1800 + map_pos[tp->diseqc];
168 
169             memset(&m_sec, 0, sizeof(recv_sec_t));
170             m_sec.voltage = map_pol[tp->pol];
171 
172             memset(&m_fep, 0, sizeof(struct dvb_frontend_parameters));
173             m_fep.frequency = tp->freq;
174             m_fep.inversion = INVERSION_AUTO;
175             m_fep.u.qpsk.symbol_rate = tp->sr;
176 
177             if (tp->sys == SYS_DVBS) {
178                 m_fep.u.qpsk.fec_inner = tp->fec;
179                 type = FE_QPSK;
180             } else {
181                 switch (tp->fec) // Handle FEC numbering exceptions
182                 {
183                 case FEC_3_5:
184                     m_fep.u.qpsk.fec_inner = 13;
185                     break;
186 
187                 case FEC_9_10:
188                     m_fep.u.qpsk.fec_inner = 14;
189                     break;
190 
191                 default:
192                     m_fep.u.qpsk.fec_inner = tp->fec;
193                 }
194 
195                 // Für DVB-S2 PSK8 oder QPSK, siehe vdr-mcli-plugin/device.c
196                 if (tp->mtype)
197                     m_fep.u.qpsk.fec_inner |= (PSK8 << 16);
198                 else
199                     m_fep.u.qpsk.fec_inner |= (QPSK_S2 << 16);
200                 type = FE_DVBS2;
201             }
202 
203             char *map_posc[] = {"", " @ 19.2E", " @ 13E", " @ 28.2E", " @ 5W"};
204             LOG("netceiver: adapter %d tuning to %d%s pol:%s sr:%d fec:%s "
205                 "delsys:%s "
206                 "mod:%s",
207                 ad->id, tp->freq / 1000, map_posc[tp->diseqc], get_pol(tp->pol),
208                 tp->sr / 1000, fe_fec[tp->fec], fe_delsys[tp->sys],
209                 fe_modulation[tp->mtype]);
210 
211             break;
212 
213             /* set roll-off */
214             // TODO: check if needed for DVB-S2 transponders
215             // unreachable code
216             //			m_fep.u.qpsk.fec_inner |= (tp->ro << 24);
217             //			break;
218 
219         case SYS_DVBC_ANNEX_A:
220             m_pos = 0xfff; /* not sure, to be tested */
221             memset(&m_sec, 0, sizeof(recv_sec_t));
222 
223             m_fep.frequency = tp->freq * 1000;
224             m_fep.inversion = INVERSION_AUTO;
225             m_fep.u.qam.fec_inner = FEC_NONE;
226             /* we enforce qam256 if qpsk is used falsely, e.g. by Elgato SAT>IP
227              * app */
228             m_fep.u.qam.modulation = tp->mtype == QPSK ? QAM_256 : tp->mtype;
229             m_fep.u.qam.symbol_rate = tp->sr;
230 
231             type = FE_QAM;
232 
233             LOG("netceiver: adapter %d tuning to %d sr:%d delsys:%s mod:%s",
234                 ad->id, tp->freq / 1000, tp->sr / 1000, fe_delsys[tp->sys],
235                 fe_modulation[tp->mtype]);
236 
237             break;
238 
239         case SYS_DVBT:
240             m_fep.frequency = tp->freq * 1000;
241             m_fep.inversion = INVERSION_AUTO;
242 
243             switch (tp->bw) {
244             case 8000000:
245                 m_fep.u.ofdm.bandwidth = BANDWIDTH_8_MHZ;
246                 break;
247             case 7000000:
248                 m_fep.u.ofdm.bandwidth = BANDWIDTH_7_MHZ;
249                 break;
250             case 6000000:
251                 m_fep.u.ofdm.bandwidth = BANDWIDTH_6_MHZ;
252                 break;
253             case 0:
254                 m_fep.u.ofdm.bandwidth = BANDWIDTH_AUTO;
255                 break;
256             case 5000000:
257                 m_fep.u.ofdm.bandwidth = BANDWIDTH_5_MHZ;
258                 break;
259             case 10000000:
260                 m_fep.u.ofdm.bandwidth = BANDWIDTH_10_MHZ;
261                 break;
262             case 1712000:
263                 m_fep.u.ofdm.bandwidth = BANDWIDTH_1_712_MHZ;
264                 break;
265             default:
266                 m_fep.u.ofdm.bandwidth = BANDWIDTH_AUTO;
267                 LOG("netceiver: DVB-T: unknown bandwith: %d", tp->bw);
268             }
269 
270             m_fep.u.ofdm.code_rate_HP = FEC_AUTO; // TBC
271             m_fep.u.ofdm.code_rate_LP = FEC_AUTO; // TBC
272 
273             switch (tp->mtype) // not properly handled by vdr-satip-plugin ?
274             {
275             case 0:
276                 m_fep.u.ofdm.constellation = QAM_AUTO;
277                 break;
278             case 6:
279                 m_fep.u.ofdm.constellation = QAM_32;
280                 break;
281             default:
282                 m_fep.u.ofdm.constellation = tp->mtype;
283             }
284 
285             m_fep.u.ofdm.transmission_mode = tp->tmode;
286             m_fep.u.ofdm.guard_interval = tp->gi;
287             m_fep.u.ofdm.hierarchy_information = HIERARCHY_NONE;
288 
289             type = FE_OFDM;
290 
291             LOG("netceiver: adapter %d tuning to %d inv: %d mtype: %d "
292                 "hprate: %d tmode: %d gi: %d bw:%d sm %d t2id %d",
293                 ad->id, tp->freq / 1000, tp->inversion, tp->mtype, tp->hprate,
294                 tp->tmode, tp->gi, tp->bw, tp->sm, tp->t2id);
295 
296             break;
297         }
298 
299         memset(m_pids, 0, sizeof(m_pids));
300         m_pids[0].pid = -1;
301 
302         /* call recv_tune of libmcli */
303         if (recv_tune(SN->ncv_rec, type, m_pos, &m_sec, &m_fep, m_pids) != 0)
304             LOG("netceiver: Tuning receiver failed");
305 
306         ad->strength = 0;
307         ad->status = 0;
308         ad->snr = 0;
309         ad->ber = 0;
310 
311         SN->want_tune = 0;
312     }
313 
314     /* activate or deactivate PIDs */
315     if (SN->want_commit) {
316         if (SN->lp) {
317             memset(m_pids, 0, sizeof(m_pids));
318             for (i = 0; i < SN->lp; i++) {
319                 m_pids[i].pid = SN->npid[i];
320                 m_pids[i].id = 0; // here we maybe have to set the SID if this
321                                   // PID is encrypted
322             }
323 
324             m_pids[i].pid = -1;
325             /* call recv_pids of libmcli to set the active PIDs */
326             usleep(10000);
327             if (recv_pids(SN->ncv_rec, m_pids))
328                 LOG("netceiver: seting PIDs failed");
329         } else {
330             /* call recv_stop of libmcli to deactivate all PIDs */
331             if (recv_stop(SN->ncv_rec))
332                 LOG("netceiver: removing all PIDs failed");
333         }
334 
335         SN->want_commit = 0;
336     }
337 
338     return 0;
339 }
340 
netcv_tune(int aid,transponder * tp)341 int netcv_tune(int aid, transponder *tp) {
342     adapter *ad = get_adapter(aid);
343     if (!ad)
344         return 1;
345 
346     SN->want_tune =
347         1; // we do not tune right now, just set the flag for netcv_commit
348     SN->want_commit = 0;
349     SN->lp = 0; // make sure number of active pids is 0 after tuning
350     return 0;
351 }
352 
netcv_delsys(int aid,int fd,fe_delivery_system_t * sys)353 fe_delivery_system_t netcv_delsys(int aid, int fd, fe_delivery_system_t *sys) {
354     return 0;
355 }
356 
find_netcv_adapter(adapter ** a)357 void find_netcv_adapter(adapter **a) {
358     int i, k, n, na;
359     netceiver_info_list_t *nc_list;
360     adapter *ad;
361     char dbuf[2048];
362 
363     /* check if network interface is available */
364     struct ifaddrs *nif, *nif1;
365 
366     if (!opts.netcv_if)
367         return;
368 
369     getifaddrs(&nif);
370 
371     nif1 = nif;
372     while (strcmp(nif1->ifa_name, opts.netcv_if) ||
373            nif1->ifa_addr->sa_family != AF_INET6) {
374         if (nif1->ifa_next == NULL) {
375             nif1 = NULL;
376             break;
377         }
378         nif1 = nif1->ifa_next;
379     }
380 
381 #define IFF_NETCV (IFF_UP | IFF_RUNNING | IFF_MULTICAST)
382     if (nif1 == NULL || (nif1->ifa_flags & IFF_NETCV) != IFF_NETCV) {
383         LOG("network interface %s not available", opts.netcv_if);
384         exit(0);
385     }
386     freeifaddrs(nif);
387 
388     /* call recv_init of libmcli to initialize the NetCeiver API */
389     if (recv_init(opts.netcv_if, 23000))
390         LOG("Netceiver init failed");
391 
392     /* Call api_sock_init to initialize the socket needed for netceiver tools */
393     if (api_sock_init(API_SOCK_NAMESPACE))
394         LOG("Netceiver API socket init failed");
395 
396     sprintf(dbuf, "REEL: Search for %d Netceiver%s on %s... ", opts.netcv_count,
397             opts.netcv_count == 1 ? "" : "s", opts.netcv_if);
398     n = 0;
399     do {
400         usleep(250000);
401         sprintf(dbuf + strlen(dbuf), "##");
402         nc_list = nc_get_list();
403     } while (nc_list->nci_num < opts.netcv_count && n++ < 19);
404     nc_lock_list();
405     sprintf(dbuf + strlen(dbuf), "\n");
406 
407     /* count available tuner types */
408     int nc_sys_c[] = {0, 0, 0, 0, 0};
409     int map_type[] = {FE_DVBS2, FE_QPSK, FE_QAM, FE_OFDM, FE_ATSC};
410     int nc_sys_t = 0;
411     for (n = 0; n < nc_list->nci_num; n++) {
412         netceiver_info_t *nci = nc_list->nci + n;
413         sprintf(dbuf + strlen(dbuf), "Found NetCeiver: %s \n", nci->uuid);
414         for (i = 0; i < nci->tuner_num; i++) {
415             sprintf(dbuf + strlen(dbuf), "	Tuner: %s, Type %d\n",
416                     nci->tuner[i].fe_info.name, nci->tuner[i].fe_info.type);
417             nc_sys_c[nci->tuner[i].fe_info.type]++;
418             nc_sys_t++;
419         }
420     }
421     LOG("%s", dbuf);
422 
423     // add netceiver tuners to the list of adapters
424     i = FE_QPSK;
425     na = a_count;
426     sprintf(dbuf, "netceiver: adding ");
427     for (n = 0; n < nc_sys_t; n++) {
428         while (i < FE_DVBS2 && nc_sys_c[map_type[i]] == 0)
429             i++;
430         nc_sys_c[map_type[i]]--;
431 
432         if (is_adapter_disabled(na)) {
433             na++;
434             continue;
435         }
436 
437         if (na >= MAX_ADAPTERS)
438             break;
439         if (!a[na])
440             a[na] = adapter_alloc();
441         if (!sn[na])
442             sn[na] = malloc1(sizeof(SNetceiver));
443 
444         ad = a[na];
445         ad->pa = 0;
446         ad->fn = 0;
447         sn[na]->want_tune = 0;
448         sn[na]->want_commit = 0;
449         sn[na]->ncv_rec = NULL;
450 
451         /* initialize signal status info */
452         ad->strength = 0;
453         ad->status = 0;
454         ad->snr = 0;
455         ad->ber = 0;
456 
457         /* register callback functions in adapter structure */
458         ad->open = netcv_open_device;
459         ad->set_pid = netcv_set_pid;
460         ad->del_filters = netcv_del_pid;
461         ad->commit = netcv_commit;
462         ad->tune = netcv_tune;
463         ad->delsys = netcv_delsys;
464         ad->post_init = NULL;
465         ad->close = netcv_close;
466         ad->type = ADAPTER_NETCV;
467 
468         /* register delivery system type */
469         for (k = 0; k < 10; k++)
470             ad->sys[k] = 0;
471         switch (map_type[i]) {
472         case FE_DVBS2:
473             ad->sys[0] = SYS_DVBS2;
474             ad->sys[1] = SYS_DVBS;
475             sprintf(dbuf + strlen(dbuf), "[AD%d DVB-S2] ", na);
476             break;
477 
478         case FE_QPSK:
479             ad->sys[0] = SYS_DVBS;
480             sprintf(dbuf + strlen(dbuf), "[AD%d DVB-S] ", na);
481             break;
482 
483         case FE_QAM:
484             ad->sys[0] = SYS_DVBC_ANNEX_A;
485             sprintf(dbuf + strlen(dbuf), "[AD%d DVB-C] ", na);
486             break;
487 
488         case FE_OFDM:
489             sprintf(dbuf + strlen(dbuf), "[AD%d DVB-T] ", na);
490             ad->sys[0] = SYS_DVBT;
491             break;
492         }
493 
494         na++; // increase number of tuner count
495         a_count = na;
496     }
497     LOG("%s", dbuf);
498     nc_unlock_list(); // netceivers appearing after this will be recognized by
499                       // libmcli but will not made available to minisatip
500 
501     for (; na < MAX_ADAPTERS; na++)
502         if (a[na])
503             a[na]->pa = a[na]->fn = -1;
504 }
505 
506 /*
507  * Handle TS data
508  * This function is called by libmcli each time a IP packet with TS packets
509  * arrives. We write the data to the write end of a pipe
510  *
511  */
512 
handle_ts(unsigned char * buffer,size_t len,void * p)513 int handle_ts(unsigned char *buffer, size_t len, void *p) {
514     SNetceiver *nc = p;
515     size_t lw;
516 
517     if (nc->lp == 0)
518         return len;
519 
520     /* simple data format check */
521     if (buffer[0] != 0x47 || len % 188 != 0) {
522         LOG("netceiver: TS data mallformed: buf[0]=0x%02x len=%lu", buffer[0],
523             len);
524         return len;
525     }
526 
527     /* write TS data to DVR pipe */
528     lw = write(nc->pwfd, buffer, len);
529     if (lw != len)
530         LOG("netceiver: not all data forwarded (%s)", strerror(errno));
531 
532     return len;
533 }
534 
535 /* Handle signal status information */
handle_ten(tra_t * ten,void * p)536 int handle_ten(tra_t *ten, void *p) {
537     adapter *ad = p;
538     recv_festatus_t *festat;
539 
540     if (ten) {
541         festat = &ten->s;
542         ad->strength = (festat->strength & 0xffff) >> 8;
543         ad->status = festat->st == 0x1f ? FE_HAS_LOCK : 0;
544         ad->snr = (festat->snr & 0xffff) >> 8;
545         ad->ber = festat->ber;
546 
547         return 0;
548     }
549 
550     return 0;
551 }
552 #endif
553