1 /*
2 * Copyright (c) 2016 - Mauro Carvalho Chehab
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU Lesser General Public License as published by
6 * the Free Software Foundation version 2.1 of the License.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program; if not, write to the Free Software
15 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16 * Or, point your browser to http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
17 */
18
19 #define _FILE_OFFSET_BITS 64
20 #define _LARGEFILE_SOURCE 1
21 #define _LARGEFILE64_SOURCE 1
22
23 #include <libudev.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <locale.h>
27 #include <unistd.h>
28 #include <string.h>
29
30 #include <config.h>
31
32 #ifdef HAVE_PTHREAD
33 # include <pthread.h>
34 #endif
35
36 #include "dvb-fe-priv.h"
37 #include "dvb-dev-priv.h"
38
39 #ifdef ENABLE_NLS
40 # include <stdio.h>
41 # include <libintl.h>
42 # define _(string) dgettext(LIBDVBV5_DOMAIN, string)
43 #else
44 # define _(string) string
45 #endif
46
47 /* taken from glibc unistd.h */
48 #ifndef TEMP_FAILURE_RETRY
49 #define TEMP_FAILURE_RETRY(expression) \
50 ({ long int __result; \
51 do __result = (long int) (expression); \
52 while (__result == -1L && errno == EINTR); \
53 __result; })
54 #endif
55
56 struct dvb_dev_local_priv {
57 dvb_dev_change_t notify_dev_change;
58
59 #ifdef HAVE_PTHREAD
60 pthread_t dev_change_id;
61 #endif
62
63 /* udev control fields */
64 int udev_fd;
65 struct udev *udev;
66 struct udev_monitor *mon;
67
68 /* private user data, used by event notifier*/
69 void *user_priv;
70 };
71
handle_device_change(struct dvb_device_priv * dvb,struct udev_device * dev,const char * syspath,const char * action)72 static int handle_device_change(struct dvb_device_priv *dvb,
73 struct udev_device *dev,
74 const char *syspath,
75 const char *action)
76 {
77 struct dvb_dev_local_priv *priv = dvb->priv;
78 struct dvb_v5_fe_parms_priv *parms = (void *)dvb->d.fe_parms;
79 struct udev_device *parent = NULL;
80 struct dvb_dev_list dev_list, *dvb_dev;
81 enum dvb_dev_change_type type;
82 const char *bus_type, *p, *sysname;
83 char *buf;
84 int i, ret;
85
86 /* remove, change, move should all remove the device first */
87 if (!strcmp(action,"add")) {
88 type = DVB_DEV_ADD;
89 } else {
90 sysname = udev_device_get_sysname(dev);
91 if (!sysname) {
92 dvb_logerr(_("udev_device_get_sysname failed"));
93 return -ENODEV;
94 }
95
96 for (i = 0; i < dvb->d.num_devices; i++) {
97 if (!strcmp(sysname, dvb->d.devices[i].sysname)) {
98 memmove(&dvb->d.devices[i],
99 &dvb->d.devices[i + 1],
100 sizeof(*dvb->d.devices) * (dvb->d.num_devices - i));
101 dvb->d.num_devices--;
102
103 if (!dvb->d.num_devices) {
104 free(dvb->d.devices);
105 dvb->d.devices = NULL;
106 } else {
107 p = realloc(dvb->d.devices,
108 sizeof(*dvb->d.devices) * dvb->d.num_devices);
109 if (!p) {
110 dvb_logerr(_("Can't remove a device from the list of DVB devices"));
111 return -ENODEV;
112 }
113 }
114 break;
115 }
116 }
117
118 /* Return, if the device was removed */
119 if (!strcmp(action,"remove")) {
120 if (priv->notify_dev_change)
121 priv->notify_dev_change(strdup(sysname),
122 DVB_DEV_REMOVE, priv->user_priv);
123 return 0;
124 }
125 type = DVB_DEV_CHANGE;
126 }
127
128 /* Fill mandatory fields: path, sysname, dvb_type, bus_type */
129 dvb_dev = &dev_list;
130 memset(dvb_dev, 0, sizeof(*dvb_dev));
131
132 if (!syspath) {
133 syspath = udev_device_get_devnode(dev);
134 if (!syspath) {
135 dvb_logwarn(_("Can't get device node filename"));
136 goto err;
137 }
138 }
139 dvb_dev->syspath = strdup(syspath);
140
141 p = udev_device_get_devnode(dev);
142 if (!p || !*p) {
143 dvb_logwarn(_("Can't get device node filename"));
144 goto err;
145 }
146 dvb_dev->path = strdup(p);
147
148 p = udev_device_get_property_value(dev, "DVB_DEVICE_TYPE");
149 if (!p)
150 goto err;
151 for (i = 0; i < dev_type_names_size; i++) {
152 if (!strcmp(p, dev_type_names[i])) {
153 dvb_dev->dvb_type = i;
154 break;
155 }
156 }
157 if (i == dev_type_names_size) {
158 dvb_logwarn(_("Ignoring device %s"), dvb_dev->path);
159 goto err;
160 }
161
162 p = udev_device_get_sysname(dev);
163 if (!p) {
164 dvb_logwarn(_("Can't get sysname for device %s"), dvb_dev->path);
165 goto err;
166 }
167 dvb_dev->sysname = strdup(p);
168
169 parent = udev_device_get_parent(dev);
170 if (!parent)
171 goto added;
172
173 bus_type = udev_device_get_subsystem(parent);
174 if (!bus_type) {
175 dvb_logwarn(_("Can't get bus type for device %s"), dvb_dev->path);
176 goto added;
177 }
178
179 ret = asprintf(&buf, "%s:%s", bus_type, udev_device_get_sysname(parent));
180 if (ret < 0) {
181 dvb_logerr(_("error %d when storing bus address"), errno);
182 goto err;
183 }
184
185 dvb_dev->bus_addr = buf;
186
187 /* Detect dvbloopback and ignore its control interface */
188 if (!strcmp(dvb_dev->bus_addr, "platform:dvbloopback")) {
189 char c = dvb_dev->path[strlen(dvb_dev->path) - 1] - '0';
190 if (c)
191 goto err;
192 }
193
194 /* Add new element */
195 dvb->d.num_devices++;
196 dvb_dev = realloc(dvb->d.devices, sizeof(*dvb->d.devices) * dvb->d.num_devices);
197 if (!dvb_dev) {
198 dvb_logerr(_("Not enough memory to store the list of DVB devices"));
199 goto err;
200 }
201
202 dvb->d.devices = dvb_dev;
203 dvb->d.devices[dvb->d.num_devices - 1] = dev_list;
204 dvb_dev = &dvb->d.devices[dvb->d.num_devices - 1];
205
206 /* Get optional per-bus fields associated with the device parent */
207 if (!strcmp(bus_type, "pci")) {
208 const char *pci_dev, *pci_vend;
209 char *p;
210
211 pci_dev = udev_device_get_sysattr_value(parent, "subsystem_device");
212 pci_vend = udev_device_get_sysattr_value(parent, "subsystem_vendor");
213
214 if (!pci_dev || !pci_vend)
215 goto added;
216
217 p = strstr(pci_dev, "0x");
218 if (p)
219 pci_dev = p + 2;
220
221 p = strstr(pci_vend, "0x");
222 if (p)
223 pci_vend = p + 2;
224
225 ret = asprintf(&dvb_dev->bus_id, "%s:%s", pci_dev, pci_vend);
226 } else if (!strcmp(bus_type, "usb") || !strcmp(bus_type, "usbdevice")) {
227 const char *vend, *prod;
228
229 vend = udev_device_get_sysattr_value(parent, "idVendor");
230 prod = udev_device_get_sysattr_value(parent, "idProduct");
231
232 if (vend && prod)
233 ret = asprintf(&dvb_dev->bus_id, "%s:%s", vend, prod);
234
235 p = udev_device_get_sysattr_value(parent,"manufacturer");
236 if (p)
237 dvb_dev->manufacturer = strdup(p);
238
239 p = udev_device_get_sysattr_value(parent,"product");
240 if (p)
241 dvb_dev->product = strdup(p);
242
243 p = udev_device_get_sysattr_value(parent, "serial");
244 if (p)
245 dvb_dev->serial = strdup(p);
246 }
247 added:
248 if (priv->notify_dev_change)
249 priv->notify_dev_change(strdup(dvb_dev->sysname), type, priv->user_priv);
250 dvb_dev_dump_device(_("Found dvb %s device: %s"), parms, dvb_dev);
251
252 return 0;
253
254 err:
255 free_dvb_dev(dvb_dev);
256 return -ENODEV;
257 }
258
259 #ifdef HAVE_PTHREAD
monitor_device_changes(void * privdata)260 static void *monitor_device_changes(void *privdata)
261 {
262 struct dvb_device_priv *dvb = privdata;
263 struct dvb_dev_local_priv *priv = dvb->priv;
264 struct udev_device *dev;
265
266 while (1) {
267 fd_set fds;
268 struct timeval tv;
269 int ret;
270
271 FD_ZERO(&fds);
272 FD_SET(priv->udev_fd, &fds);
273 tv.tv_sec = 1;
274 tv.tv_usec = 0;
275
276 ret = select(priv->udev_fd + 1, &fds, NULL, NULL, &tv);
277
278 /* Check if our file descriptor has received data. */
279 if (ret > 0 && FD_ISSET(priv->udev_fd, &fds)) {
280 dev = udev_monitor_receive_device(priv->mon);
281 if (dev) {
282 const char *action = udev_device_get_action(dev);
283 handle_device_change(dvb, dev, NULL, action);
284 }
285 }
286 }
287 return NULL;
288 }
289 #endif
290
dvb_local_find(struct dvb_device_priv * dvb,dvb_dev_change_t handler,void * user_priv)291 static int dvb_local_find(struct dvb_device_priv *dvb,
292 dvb_dev_change_t handler, void *user_priv)
293 {
294 struct dvb_v5_fe_parms_priv *parms = (void *)dvb->d.fe_parms;
295 struct dvb_dev_local_priv *priv = dvb->priv;
296 struct udev_enumerate *enumerate;
297 struct udev_list_entry *devices, *dev_list_entry;
298 struct udev_device *dev;
299
300 /* Free a previous list of devices */
301 if (dvb->d.num_devices)
302 dvb_dev_free_devices(dvb);
303
304 /* Create the udev object */
305 priv->udev = udev_new();
306 if (!priv->udev) {
307 dvb_logerr(_("Can't create an udev object\n"));
308 return -ENOMEM;
309 }
310
311 priv->user_priv = user_priv;
312 priv->notify_dev_change = handler;
313 if (priv->notify_dev_change) {
314 #ifndef HAVE_PTHREAD
315 dvb_logerr("Need to be compiled with pthreads for monitor");
316 return -EINVAL;
317 #else
318 /* Set up a monitor to monitor dvb devices */
319 priv->mon = udev_monitor_new_from_netlink(priv->udev, "udev");
320 udev_monitor_filter_add_match_subsystem_devtype(priv->mon, "dvb", NULL);
321 udev_monitor_enable_receiving(priv->mon);
322 priv->udev_fd = udev_monitor_get_fd(priv->mon);
323 #endif
324 }
325
326 /* Create a list of the devices in the 'dvb' subsystem. */
327 enumerate = udev_enumerate_new(priv->udev);
328 udev_enumerate_add_match_subsystem(enumerate, "dvb");
329 udev_enumerate_scan_devices(enumerate);
330 devices = udev_enumerate_get_list_entry(enumerate);
331
332 udev_list_entry_foreach(dev_list_entry, devices) {
333 const char *syspath;
334
335 syspath = udev_list_entry_get_name(dev_list_entry);
336
337 dev = udev_device_new_from_syspath(priv->udev, syspath);
338 handle_device_change(dvb, dev, syspath, "add");
339 udev_device_unref(dev);
340 }
341
342 /* Free the enumerator object */
343 udev_enumerate_unref(enumerate);
344
345 /* Begin monitoring udev events */
346 #ifdef HAVE_PTHREAD
347 if (priv->notify_dev_change) {
348 int ret;
349
350 ret = pthread_create(&priv->dev_change_id, NULL,
351 monitor_device_changes, dvb);
352 if (ret < 0) {
353 dvb_perror("pthread_create");
354 return -1;
355 }
356 }
357 #endif
358 if (!priv->notify_dev_change) {
359 udev_unref(priv->udev);
360 priv->udev = NULL;
361 }
362
363 return 0;
364 }
365
dvb_local_stop_monitor(struct dvb_device_priv * dvb)366 static int dvb_local_stop_monitor(struct dvb_device_priv *dvb)
367 {
368 #ifdef HAVE_PTHREAD
369 struct dvb_dev_local_priv *priv = dvb->priv;
370
371 if (priv->notify_dev_change) {
372 pthread_cancel(priv->dev_change_id);
373 udev_unref(priv->udev);
374 }
375 #endif
376
377 return 0;
378 }
379
dvb_local_seek_by_adapter(struct dvb_device_priv * dvb,unsigned int adapter,unsigned int num,enum dvb_dev_type type)380 struct dvb_dev_list *dvb_local_seek_by_adapter(struct dvb_device_priv *dvb,
381 unsigned int adapter,
382 unsigned int num,
383 enum dvb_dev_type type)
384 {
385 struct dvb_v5_fe_parms_priv *parms = (void *)dvb->d.fe_parms;
386 int ret, i;
387 char *p;
388
389 if (type > dev_type_names_size){
390 dvb_logerr(_("Unexpected device type found!"));
391 return NULL;
392 }
393
394 ret = asprintf(&p, "dvb%d.%s%d", adapter, dev_type_names[type], num);
395 if (ret < 0) {
396 dvb_logerr(_("error %d when seeking for device's filename"),
397 errno);
398 return NULL;
399 }
400 for (i = 0; i < dvb->d.num_devices; i++) {
401 if (!strcmp(p, dvb->d.devices[i].sysname)) {
402 free(p);
403 dvb_dev_dump_device(_("Selected dvb %s device: %s"),
404 parms, &dvb->d.devices[i]);
405 return &dvb->d.devices[i];
406 }
407 }
408
409 dvb_logwarn(_("device %s not found"), p);
410 return NULL;
411 }
412
dvb_local_get_dev_info(struct dvb_device_priv * dvb,const char * sysname)413 struct dvb_dev_list *dvb_local_get_dev_info(struct dvb_device_priv *dvb,
414 const char *sysname)
415 {
416 struct dvb_v5_fe_parms_priv *parms = (void *)dvb->d.fe_parms;
417 int i;
418
419 if (!sysname) {
420 dvb_logerr(_("Device not specified"));
421 return NULL;
422 }
423
424 for (i = 0; i < dvb->d.num_devices; i++) {
425 if (!strcmp(sysname, dvb->d.devices[i].sysname)) {
426 return &dvb->d.devices[i];
427 }
428 }
429
430 dvb_logerr(_("Can't find device %s"), sysname);
431 return NULL;
432 }
433
434 static struct dvb_open_descriptor
dvb_local_open(struct dvb_device_priv * dvb,const char * sysname,int flags)435 *dvb_local_open(struct dvb_device_priv *dvb,
436 const char *sysname, int flags)
437 {
438 struct dvb_v5_fe_parms_priv *parms = (void *)dvb->d.fe_parms;
439 struct dvb_dev_list *dev;
440 struct dvb_open_descriptor *open_dev, *cur;
441 int ret;
442
443 dev = dvb_local_get_dev_info(dvb, sysname);
444 if (!dev)
445 return NULL;
446
447 open_dev = calloc(1, sizeof(*open_dev));
448 if (!open_dev) {
449 dvb_perror("Can't create file descriptor");
450 return NULL;
451 }
452
453 if (dev->dvb_type == DVB_DEVICE_FRONTEND) {
454 /*
455 * The frontend API was designed for sync frontend access.
456 * It is not ready to handle async frontend access.
457 * However, dvbloopback is a different beast: it only works
458 * if opened with O_NONBLOCK.
459 * Also, support for FE_SET_PROPERTY/FE_GET_PROPERTY
460 * is broken with dvbloopback and recent Kernels, as it
461 * doesn't copy from/to usermemory properly.
462 */
463 if (!strcmp(dev->bus_addr, "platform:dvbloopback")) {
464 dvb_logwarn(_("Detected dvbloopback"));
465 flags |= O_NONBLOCK;
466 } else {
467 flags &= ~O_NONBLOCK;
468 }
469
470 ret = dvb_fe_open_fname(parms, strdup(dev->path), flags);
471 if (ret) {
472 free(open_dev);
473 return NULL;
474 }
475 ret = parms->fd;
476 } else {
477 /* We don't need special handling for other DVB device types */
478 ret = open(dev->path, flags);
479 if (ret == -1) {
480 dvb_logerr(_("Can't open %s with flags %d: %d %m"),
481 dev->path, flags, errno);
482 free(open_dev);
483 return NULL;
484 }
485 }
486
487 /* Add the fd to the open descriptor's list */
488 open_dev->fd = ret;
489 open_dev->dev = dev;
490 open_dev->dvb = dvb;
491
492 cur = &dvb->open_list;
493 while (cur->next)
494 cur = cur->next;
495 cur->next = open_dev;
496
497 return open_dev;
498 }
499
dvb_local_close(struct dvb_open_descriptor * open_dev)500 static int dvb_local_close(struct dvb_open_descriptor *open_dev)
501 {
502 struct dvb_dev_list *dev = open_dev->dev;
503 struct dvb_device_priv *dvb = open_dev->dvb;
504 struct dvb_v5_fe_parms_priv *parms = (void *)dvb->d.fe_parms;
505 struct dvb_open_descriptor *cur;
506
507 if (dev->dvb_type == DVB_DEVICE_FRONTEND)
508 __dvb_fe_close(parms);
509 else {
510 if (dev->dvb_type == DVB_DEVICE_DEMUX)
511 dvb_dev_dmx_stop(open_dev);
512
513 close(open_dev->fd);
514 }
515
516 for (cur = &dvb->open_list; cur->next; cur = cur->next) {
517 if (cur->next == open_dev) {
518 cur->next = open_dev->next;
519 free(open_dev);
520 return 0;
521 }
522 }
523
524 /* Should never happen */
525 dvb_logerr(_("Couldn't free device\n"));
526
527 return -ENODEV;
528 }
529
530 #define MAX_TIME 10 /* 1.0 seconds */
531
532 #define xioctl(fh, request, arg...) ({ \
533 int __rc; \
534 struct timespec __start, __end; \
535 \
536 clock_gettime(CLOCK_MONOTONIC, &__start); \
537 do { \
538 __rc = ioctl(fh, request, ##arg); \
539 if (__rc != -1) \
540 break; \
541 if ((errno != EINTR) && (errno != EAGAIN)) \
542 break; \
543 clock_gettime(CLOCK_MONOTONIC, &__end); \
544 if (__end.tv_sec * 10 + __end.tv_nsec / 100000000 > \
545 __start.tv_sec * 10 + __start.tv_nsec / 100000000 + \
546 MAX_TIME) \
547 break; \
548 } while (1); \
549 \
550 __rc; \
551 })
552
dvb_local_dmx_stop(struct dvb_open_descriptor * open_dev)553 static int dvb_local_dmx_stop(struct dvb_open_descriptor *open_dev)
554 {
555 struct dvb_dev_list *dev = open_dev->dev;
556 struct dvb_device_priv *dvb = open_dev->dvb;
557 struct dvb_v5_fe_parms_priv *parms = (void *)dvb->d.fe_parms;
558 int ret, fd = open_dev->fd;
559
560 if (dev->dvb_type != DVB_DEVICE_DEMUX)
561 return -EINVAL;
562
563 ret = xioctl(fd, DMX_STOP);
564 if (ret == -1) {
565 dvb_perror(_("DMX_STOP failed"));
566 return -errno;
567 }
568
569 return 0;
570 }
571
dvb_local_set_bufsize(struct dvb_open_descriptor * open_dev,int buffersize)572 static int dvb_local_set_bufsize(struct dvb_open_descriptor *open_dev,
573 int buffersize)
574 {
575 struct dvb_dev_list *dev = open_dev->dev;
576 struct dvb_device_priv *dvb = open_dev->dvb;
577 struct dvb_v5_fe_parms_priv *parms = (void *)dvb->d.fe_parms;
578 int fd = open_dev->fd;
579
580 if (dev->dvb_type != DVB_DEVICE_DEMUX && dev->dvb_type != DVB_DEVICE_DVR)
581 return -EINVAL;
582
583 if (xioctl(fd, DMX_SET_BUFFER_SIZE, buffersize) == -1) {
584 dvb_perror(_("DMX_SET_BUFFER_SIZE failed"));
585 return -errno;
586 }
587
588 return 0;
589 }
590
dvb_local_read(struct dvb_open_descriptor * open_dev,void * buf,size_t count)591 static ssize_t dvb_local_read(struct dvb_open_descriptor *open_dev,
592 void *buf, size_t count)
593 {
594 struct dvb_dev_list *dev = open_dev->dev;
595 struct dvb_device_priv *dvb = open_dev->dvb;
596 struct dvb_v5_fe_parms_priv *parms = (void *)dvb->d.fe_parms;
597 int fd = open_dev->fd;
598 ssize_t ret;
599
600 if (dev->dvb_type != DVB_DEVICE_DEMUX && dev->dvb_type != DVB_DEVICE_DVR) {
601 dvb_logerr("Trying to read from an invalid device type on fd #%d", fd);
602 return -EINVAL;
603 }
604
605 /*
606 * As we opened dvbloopback on non-blocking mode, we need to
607 * check if read is ready, in order to emulate blocking mode
608 */
609 if (!strcmp(dev->bus_addr, "platform:dvbloopback")) {
610 fd_set rset;
611 fd_set eset;
612
613 FD_ZERO(&rset);
614 FD_SET(fd, &rset);
615 FD_ZERO(&eset);
616 FD_SET(fd, &eset);
617 ret = TEMP_FAILURE_RETRY(select(FD_SETSIZE,
618 &rset, NULL, &eset, NULL));
619 if (ret == -1) {
620 if (errno != EOVERFLOW)
621 dvb_perror("read()");
622 return -errno;
623 }
624 }
625
626 ret = TEMP_FAILURE_RETRY(read(fd, buf, count));
627 if (ret == -1) {
628 if (errno != EOVERFLOW && errno != EAGAIN)
629 dvb_perror("read()");
630 return -errno;
631 }
632
633 return ret;
634 }
635
dvb_local_dmx_set_pesfilter(struct dvb_open_descriptor * open_dev,int pid,dmx_pes_type_t type,dmx_output_t output,int bufsize)636 static int dvb_local_dmx_set_pesfilter(struct dvb_open_descriptor *open_dev,
637 int pid, dmx_pes_type_t type,
638 dmx_output_t output, int bufsize)
639 {
640 struct dvb_dev_list *dev = open_dev->dev;
641 struct dvb_device_priv *dvb = open_dev->dvb;
642 struct dvb_v5_fe_parms_priv *parms = (void *)dvb->d.fe_parms;
643 struct dmx_pes_filter_params pesfilter;
644 int fd = open_dev->fd;
645
646 if (dev->dvb_type != DVB_DEVICE_DEMUX)
647 return -EINVAL;
648
649 /* Failing here is not fatal, so no need to handle error condition */
650 if (bufsize)
651 dvb_dev_set_bufsize(open_dev, bufsize);
652
653 memset(&pesfilter, 0, sizeof(pesfilter));
654
655 pesfilter.pid = pid;
656 pesfilter.input = DMX_IN_FRONTEND;
657 pesfilter.output = output;
658 pesfilter.pes_type = type;
659 pesfilter.flags = DMX_IMMEDIATE_START;
660
661 if (xioctl(fd, DMX_SET_PES_FILTER, &pesfilter) == -1) {
662 dvb_logerr(_("DMX_SET_PES_FILTER failed (PID = 0x%04x): %d %m"),
663 pid, errno);
664 return -errno;
665 }
666
667 return 0;
668 }
669
dvb_local_dmx_set_section_filter(struct dvb_open_descriptor * open_dev,int pid,unsigned filtsize,unsigned char * filter,unsigned char * mask,unsigned char * mode,unsigned int flags)670 static int dvb_local_dmx_set_section_filter(struct dvb_open_descriptor *open_dev,
671 int pid, unsigned filtsize,
672 unsigned char *filter,
673 unsigned char *mask,
674 unsigned char *mode,
675 unsigned int flags)
676 {
677 struct dvb_dev_list *dev = open_dev->dev;
678 struct dvb_device_priv *dvb = open_dev->dvb;
679 struct dvb_v5_fe_parms_priv *parms = (void *)dvb->d.fe_parms;
680 struct dmx_sct_filter_params sctfilter;
681 int fd = open_dev->fd;
682
683 if (dev->dvb_type != DVB_DEVICE_DEMUX)
684 return -EINVAL;
685
686 if (filtsize > DMX_FILTER_SIZE)
687 filtsize = DMX_FILTER_SIZE;
688
689 memset(&sctfilter, 0, sizeof(sctfilter));
690
691 sctfilter.pid = pid;
692
693 if (filter)
694 memcpy(sctfilter.filter.filter, filter, filtsize);
695 if (mask)
696 memcpy(sctfilter.filter.mask, mask, filtsize);
697 if (mode)
698 memcpy(sctfilter.filter.mode, mode, filtsize);
699
700 sctfilter.flags = flags;
701
702 if (xioctl(fd, DMX_SET_FILTER, &sctfilter) == -1) {
703 dvb_logerr(_("DMX_SET_FILTER failed (PID = 0x%04x): %d %m"),
704 pid, errno);
705 return -errno;
706 }
707
708 return 0;
709 }
710
dvb_local_dmx_get_pmt_pid(struct dvb_open_descriptor * open_dev,int sid)711 static int dvb_local_dmx_get_pmt_pid(struct dvb_open_descriptor *open_dev, int sid)
712 {
713 struct dvb_dev_list *dev = open_dev->dev;
714 struct dvb_device_priv *dvb = open_dev->dvb;
715 struct dvb_v5_fe_parms_priv *parms = (void *)dvb->d.fe_parms;
716 struct dmx_sct_filter_params f;
717 unsigned char buft[4096];
718 unsigned char *buf = buft;
719 int fd = open_dev->fd;
720 int count;
721 int pmt_pid = 0;
722 int patread = 0;
723 int section_length;
724
725 if (dev->dvb_type != DVB_DEVICE_DEMUX)
726 return -EINVAL;
727
728 memset(&f, 0, sizeof(f));
729 f.pid = 0;
730 f.filter.filter[0] = 0x00;
731 f.filter.mask[0] = 0xff;
732 f.timeout = 0;
733 f.flags = DMX_IMMEDIATE_START | DMX_CHECK_CRC;
734
735 if (xioctl(fd, DMX_SET_FILTER, &f) == -1) {
736 dvb_perror("ioctl DMX_SET_FILTER failed");
737 return -errno;
738 }
739
740 while (!patread){
741 count = TEMP_FAILURE_RETRY(read(fd, buf, sizeof(buft)));
742 if (count < 0 && errno == EOVERFLOW)
743 count = TEMP_FAILURE_RETRY(read(fd, buf, sizeof(buft)));
744 if (count < 0) {
745 dvb_perror("read_sections: read error");
746 return -errno;
747 }
748
749 section_length = ((buf[1] & 0x0f) << 8) | buf[2];
750 if (count != section_length + 3)
751 continue;
752
753 buf += 8;
754 section_length -= 8;
755
756 patread = 1; /* assumes one section contains the whole pat */
757 while (section_length > 0) {
758 int service_id = (buf[0] << 8) | buf[1];
759 if (service_id == sid) {
760 pmt_pid = ((buf[2] & 0x1f) << 8) | buf[3];
761 section_length = 0;
762 }
763 buf += 4;
764 section_length -= 4;
765 }
766 }
767
768 return pmt_pid;
769 }
770
dvb_local_scan(struct dvb_open_descriptor * open_dev,struct dvb_entry * entry,check_frontend_t * check_frontend,void * args,unsigned other_nit,unsigned timeout_multiply)771 static struct dvb_v5_descriptors *dvb_local_scan(struct dvb_open_descriptor *open_dev,
772 struct dvb_entry *entry,
773 check_frontend_t *check_frontend,
774 void *args,
775 unsigned other_nit,
776 unsigned timeout_multiply)
777 {
778 struct dvb_dev_list *dev = open_dev->dev;
779 struct dvb_device_priv *dvb = open_dev->dvb;
780 struct dvb_v5_fe_parms_priv *parms = (void *)dvb->d.fe_parms;
781 struct dvb_v5_descriptors *desc;
782 int fd = open_dev->fd;
783
784 if (dev->dvb_type != DVB_DEVICE_DEMUX) {
785 dvb_logerr(_("dvb_dev_scan: expecting a demux descriptor"));
786 return NULL;
787 }
788
789 desc = dvb_scan_transponder(dvb->d.fe_parms, entry, fd, check_frontend,
790 args, other_nit, timeout_multiply);
791
792 return desc;
793 }
794
795 /* Frontend functions that can be overriden */
796
dvb_local_fe_set_sys(struct dvb_v5_fe_parms * p,fe_delivery_system_t sys)797 int dvb_local_fe_set_sys(struct dvb_v5_fe_parms *p, fe_delivery_system_t sys)
798 {
799 return __dvb_set_sys(p, sys);
800 }
801
dvb_local_fe_get_parms(struct dvb_v5_fe_parms * p)802 int dvb_local_fe_get_parms(struct dvb_v5_fe_parms *p)
803 {
804 return __dvb_fe_get_parms(p);
805 }
806
dvb_local_fe_set_parms(struct dvb_v5_fe_parms * p)807 int dvb_local_fe_set_parms(struct dvb_v5_fe_parms *p)
808 {
809 return __dvb_fe_set_parms(p);
810 }
811
dvb_local_fe_get_stats(struct dvb_v5_fe_parms * p)812 int dvb_local_fe_get_stats(struct dvb_v5_fe_parms *p)
813 {
814 return __dvb_fe_get_stats(p);
815 }
816
817
dvb_dev_local_free(struct dvb_device_priv * dvb)818 static void dvb_dev_local_free(struct dvb_device_priv *dvb)
819 {
820 struct dvb_dev_local_priv *priv = dvb->priv;
821
822 dvb_local_stop_monitor(dvb);
823
824 free(priv);
825 }
826
dvb_local_get_fd(struct dvb_open_descriptor * open_dev)827 static int dvb_local_get_fd(struct dvb_open_descriptor *open_dev)
828 {
829 return open_dev->fd;
830 }
831
832 /* Initialize for local usage */
dvb_dev_local_init(struct dvb_device_priv * dvb)833 void dvb_dev_local_init(struct dvb_device_priv *dvb)
834 {
835 struct dvb_dev_ops *ops = &dvb->ops;
836
837 dvb->priv = calloc(1, sizeof(struct dvb_dev_local_priv));
838
839 ops->find = dvb_local_find;
840 ops->seek_by_adapter = dvb_local_seek_by_adapter;
841 ops->get_dev_info = dvb_local_get_dev_info;
842 ops->stop_monitor = dvb_local_stop_monitor;
843 ops->open = dvb_local_open;
844 ops->close = dvb_local_close;
845 ops->get_fd = dvb_local_get_fd;
846
847 ops->dmx_stop = dvb_local_dmx_stop;
848 ops->set_bufsize = dvb_local_set_bufsize;
849 ops->read = dvb_local_read;
850 ops->dmx_set_pesfilter = dvb_local_dmx_set_pesfilter;
851 ops->dmx_set_section_filter = dvb_local_dmx_set_section_filter;
852 ops->dmx_get_pmt_pid = dvb_local_dmx_get_pmt_pid;
853
854 ops->scan = dvb_local_scan;
855
856 ops->fe_set_sys = dvb_local_fe_set_sys;
857 ops->fe_get_parms = dvb_local_fe_get_parms;
858 ops->fe_set_parms = dvb_local_fe_set_parms;
859 ops->fe_get_stats = dvb_local_fe_get_stats;
860
861 ops->free = dvb_dev_local_free;
862 }
863