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