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