Skip site navigation (1)Skip section navigation (2)
Date:      Thu, 26 Aug 2010 20:35:43 +0200
From:      Juergen Lock <nox@jelal.kn-bremen.de>
To:        Hans Petter Selasky <hselasky@freebsd.org>
Cc:        me@janh.de, kde@freebsd.org, Juergen Lock <nox@jelal.kn-bremen.de>, freebsd-multimedia@freebsd.org, Joe Marcus Clarke <marcus@freebsd.org>, gnome@freebsd.org
Subject:   Re: kaffeine-1.0 and webcamd based DVB-T?
Message-ID:  <20100826183543.GA39319@triton8.kn-bremen.de>
In-Reply-To: <201008252350.05592.hselasky@freebsd.org>
References:  <4C66C4BC.4040504@janh.de> <4C7435AA.50805@freebsd.org> <20100825214141.GA8685@triton8.kn-bremen.de> <201008252350.05592.hselasky@freebsd.org>

next in thread | previous in thread | raw e-mail | index | archive | help
On Wed, Aug 25, 2010 at 11:50:05PM +0200, Hans Petter Selasky wrote:
> On Wednesday 25 August 2010 23:41:41 Juergen Lock wrote:
> > On Tue, Aug 24, 2010 at 05:12:10PM -0400, Joe Marcus Clarke wrote:
> > > On 8/24/10 5:04 PM, Hans Petter Selasky wrote:
> > > > On Tuesday 24 August 2010 22:21:18 Juergen Lock wrote:
> > > >> #! /bin/sh
> > > >> # add PCTV 452e Sat HDTV Pro USB to hal as /dev/dvb/adapter0
> > > >> hal-device --add usb_device_2304_21f_noserial_dvb_0 <<EOF
> > > >> dvb.device = '/dev/dvb/adapter0/demux0'  (string)
> > > >> info.capabilities = {'dvb'} (string list)
> > > >> info.category = 'dvb'  (string)
> > > >> info.parent =
> > > >> '/org/freedesktop/Hal/devices/usb_device_2304_21f_noserial'  (string)
> > > >> info.product = 'DVB Device'  (string)
> > > >> info.subsystem = 'dvb'  (string)
> > > >> EOF
> > > >> hal-device --add usb_device_2304_21f_noserial_dvb_1 <<EOF
> > > >> dvb.device = '/dev/dvb/adapter0/dvr0'  (string)
> > > >> info.capabilities = {'dvb'} (string list)
> > > >> info.category = 'dvb'  (string)
> > > >> info.parent =
> > > >> '/org/freedesktop/Hal/devices/usb_device_2304_21f_noserial'  (string)
> > > >> info.product = 'DVB Device'  (string)
> > > >> info.subsystem = 'dvb'  (string)
> > > >> EOF
> > > >> hal-device --add usb_device_2304_21f_noserial_dvb_2 <<EOF
> > > > 
> > > > Hi,
> > > > 
> > > > Could you have changed this into "execve()" calls (man execve) and add
> > > > these to webcamd.c whenever cuse_dev_create() is called? Also for
> > > > /dev/videoX entries. Then we don't need to patch HAL?
> > > 
> > > Yeah, if webcamd can notify hal that new dvb and v4l devices are
> > > available (and what those devices' capabilities are) then we can remove
> > > the patches from hal.
> > 
> > Ok I now made that an extra process (so it can open() /dev/videoX
> > normally and also that way webcamd itself doesn't have to link
> > libhal and possible problems with fork() and threads are avoided),
> > webcamd then just feeds it the device nodes on stdin.
> > 
> >  Untested with v4l devices since I don't have one here, and
> > I also built the helper manually for now and put it into PATH.
> > And the code can still be cleaned up...
> > 
> >  helper built as:
> > 
> > 	cc -o webcamd-hal-helper -Wall webcamd-hal-helper.c $(pkg-config --cflags
> > hal) $(pkg-config --libs hal) -I/usr/local/include
> > 
> >  Patch also at:
> > 
> > 	http://people.freebsd.org/~nox/tmp/webcamd-hal.patch
> > 
> >  HTH, :)
> > 	Juergen
> 
> Looks good.
> 
> Could you also register an atexit() function, that cleans up the HAL registry 
> when webcamd exits?

Ok, done.  (Tho that already happened automagically when unplugging
the device because the /dev/dvb/adapterX/* are registered as childs
of the usb device's hal entry...)

 Patch also at:

	http://people.freebsd.org/~nox/tmp/webcamd-hal.patch

Index: webcamd.c
===================================================================
--- webcamd.c	(revision 1621)
+++ webcamd.c	(working copy)
@@ -37,6 +37,11 @@
 
 #include <cuse4bsd.h>
 
+#ifndef HALHELPER
+/* Helper process that feeds webcamd cuse4bsd device nodes into hal */
+#define HALHELPER "webcamd-hal-helper"
+#endif
+
 static cuse_open_t v4b_open;
 static cuse_close_t v4b_close;
 static cuse_read_t v4b_read;
@@ -76,6 +81,9 @@
 static int do_fork = 0;
 static int do_realtime = 1;
 static struct pidfh *local_pid = NULL;
+#ifdef HALHELPER
+static FILE *fp_halhelper = NULL;
+#endif
 
 char	global_fw_prefix[128] = {"/boot/modules"};
 
@@ -309,6 +317,13 @@
 			printf("Creating /dev/");
 			printf(devnames[n / F_V4B_SUBDEV_MAX], temp);
 			printf("\n");
+#ifdef HALHELPER
+			if (fp_halhelper) {
+				fprintf(fp_halhelper, "/dev/");
+				fprintf(fp_halhelper, devnames[n / F_V4B_SUBDEV_MAX], temp);
+				fprintf(fp_halhelper, "\n");
+			}
+#endif
 
 			ndev++;
 		}
@@ -354,6 +369,10 @@
 		pidfile_remove(local_pid);
 		local_pid = NULL;
 	}
+#ifdef HALHELPER
+	if (fp_halhelper)
+		pclose(fp_halhelper);
+#endif
 }
 
 int
@@ -375,6 +394,24 @@
 		pidfile_write(local_pid);
 	}
 
+#ifdef HALHELPER
+	snprintf(buf, sizeof(buf), "%d", bus);
+	setenv("HAL_PROP_USB_BUS_NUMBER", buf, 1);
+	snprintf(buf, sizeof(buf), "%d", addr);
+	setenv("HAL_PROP_USB_PORT_NUMBER", buf, 1);
+	snprintf(buf, sizeof(buf), "%d", 0);
+	setenv("HAL_PROP_USB_INTERFACE_NUMBER", buf, 1);
+
+	if ((fp_halhelper = popen(HALHELPER, "w")) == NULL) {
+		/* XXX */
+		return (EEXIST);
+	}
+	/* Use line buffering */
+	setvbuf(fp_halhelper, (char *)NULL, _IOLBF, 0);
+	/* Keep going if helper exits (e.g. because hal is not running...) */
+	signal(SIGPIPE, SIG_IGN);
+#endif
+
 	printf("Attached ugen%d.%d[%d] to cuse unit %d\n",
 	    bus, addr, index, u_videodev);
 
--- /dev/null	2010-08-26 20:22:01.000000000 +0200
+++ webcamd-hal-helper.c	2010-08-26 20:00:07.000000000 +0200
@@ -0,0 +1,348 @@
+/***************************************************************************
+ * CVSID: $Id$
+ *
+ * webcamd-hal-helper.c : Notify hal of webcamd cuse4bsd device nodes
+ * (yes this is a little ugly and could use some cleanup but it works
+ * and I'm lazy :)
+ * Hacked together by : Juergen Lock <nox@FreeBSD.org>
+ *
+ * Takes env vars HAL_PROP_USB_BUS_NUMBER, HAL_PROP_USB_PORT_NUMBER,
+ * and HAL_PROP_USB_INTERFACE_NUMBER to identify the usb device in
+ * question, reads names of v4l/dvb device nodes to add to hal on
+ * stdin, and accepts -n as arg in which case it doesn't clean up
+ * (remove new device nodes from hal) on eof/exit/signal.
+ *
+ * Build as:
+ *	cc -o webcamd-hal-helper -Wall webcamd-hal-helper.c $(pkg-config --cflags hal) $(pkg-config --libs hal) -I/usr/local/include
+ *
+ * Uses code from...
+ *
+ * probe-video4linux.c : Probe video4linux devices
+ * Adapted for FreeBSD by : Joe Marcus Clarke <marcus@FreeBSD.org>
+ *
+ * Copyright (C) 2007 Nokia Corporation
+ *
+ * Licensed under the Academic Free License version 2.1
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ **************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#  include <config.h>
+#endif
+
+#include <sys/types.h>
+#include <sys/user.h>
+#include <sys/sysctl.h>
+#include <sys/time.h>
+#include <sys/ioctl.h>
+#include <linux/videodev.h>
+#include <linux/videodev2.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "hal/libhal.h"
+
+#define DEBUG
+
+#ifdef DEBUG
+#define h_info printf
+#else
+#define h_info(...) /* */
+#endif
+
+typedef struct {
+	char *udi;
+	char *real_udi;
+} new_dev_t;
+
+static char *hal_udi = NULL;
+static int dvbindex = 0;
+static LibHalContext *hal_ctx = NULL;
+
+static char *
+find_usb_udi (LibHalContext *ctx, int bus, int addr)
+{
+	int i, num_devices;
+
+	char **u_devs = libhal_manager_find_device_string_match
+		(ctx, "info.bus", "usb_device", &num_devices, NULL);
+
+	if (!u_devs || !num_devices)
+		return NULL;
+
+	for (i = 0; i < num_devices; ++i) {
+		if (libhal_device_get_property_int(ctx, u_devs[i],
+			"usb_device.bus_number", NULL) == bus &&
+		    libhal_device_get_property_int(ctx, u_devs[i],
+			"usb_device.port_number", NULL) == addr)
+			return u_devs[i];
+	}
+	return NULL;
+}
+
+static void
+cleanup ()
+{
+	int i;
+
+	if (!hal_udi || !dvbindex)
+		return;
+	for (i = 0; i < dvbindex; ++i) {
+		char *dvbudi;
+
+		if (asprintf(&dvbudi, "%s_dvb_%d", hal_udi, i) == -1) {
+			perror("asprintf");
+			break;
+		}
+		h_info ("Removing %s\n", dvbudi);
+		libhal_remove_device(hal_ctx, dvbudi, NULL);
+		free(dvbudi);
+	}
+	dvbindex = 0;
+}
+
+static void
+termsig (int unused)
+{
+	cleanup();
+	exit(0);
+}
+
+int
+main (int argc, char **argv)
+{
+	int ret = 1;
+	int fd = -1;
+	int bus = -1;
+	int addr = -1;
+	int intf = -1;
+	char *device_file = NULL;
+	char *busstr;
+	char *addrstr;
+	char *intfstr;
+	struct video_capability v1cap;
+	struct v4l2_capability v2cap;
+
+	DBusError error;
+	DBusConnection *conn;
+	LibHalChangeSet *cset;
+
+	busstr = getenv ("HAL_PROP_USB_BUS_NUMBER");
+	if (! busstr)
+		goto out;
+	addrstr = getenv ("HAL_PROP_USB_PORT_NUMBER");
+	if (! addrstr)
+		goto out;
+	intfstr = getenv ("HAL_PROP_USB_INTERFACE_NUMBER");
+	if (! intfstr)
+		goto out;
+
+	bus = atoi (busstr);
+	addr = atoi (addrstr);
+	intf = atoi (intfstr);
+
+	if (intf != 0)
+		goto out;
+
+	dbus_error_init(&error);
+	if (!(conn = dbus_bus_get(DBUS_BUS_SYSTEM, &error))) {
+		fprintf(stderr, "error: dbus_bus_get: %s: %s\n", error.name, error.message);
+		LIBHAL_FREE_DBUS_ERROR (&error);
+		return 2;
+	}
+
+	if (!(hal_ctx = libhal_ctx_new())) return 3;
+	if (!libhal_ctx_set_dbus_connection(hal_ctx, conn)) return 4;
+	if (!libhal_ctx_init(hal_ctx, &error)) {
+		if (dbus_error_is_set(&error)) {
+			fprintf (stderr, "error: libhal_ctx_init: %s: %s\n", error.name, error.message);
+			dbus_error_free (&error);
+		}
+		fprintf (stderr, "Could not initialise connection to hald.\n"
+				 "Normally this means the HAL daemon (hald) is not running or not ready.\n");
+		return 5;
+	}
+
+	hal_udi = find_usb_udi (hal_ctx, bus, addr);
+	if (hal_udi == NULL) {
+		fprintf(stderr, "Device not found in hal: usb bus %d, address %d\n",
+			bus, addr);
+		goto out;
+	}
+
+	char line[0x1000];
+
+	/* give a meaningful process title for ps(1) */
+	setproctitle("%s (bus: %i, addr: %i)", hal_udi, bus, addr);
+
+	if (argc < 2 || strcmp(argv[1], "-n")) {
+		atexit(&cleanup);
+		signal(SIGTERM, &termsig);
+		signal(SIGINT, &termsig);
+	}
+
+	while (42) {
+		size_t len;
+
+		device_file = fgets(line, sizeof line, stdin);
+		if (device_file == NULL || !(len = strlen(device_file)) ||
+		    device_file[len - 1] != '\n')
+			break;
+		device_file[len - 1] = '\0';
+
+		if (!strncmp(device_file, "/dev/video", sizeof "/dev/video" - 1)) {
+			cset = libhal_device_new_changeset (hal_udi);
+
+			h_info ("Doing probe-video4linux-hal for %s (udi=%s)\n", device_file, hal_udi);
+
+			fd = open (device_file, O_RDONLY);
+			if (fd < 0) {
+				fprintf(stderr, "Cannot open %s: %s\n", device_file, strerror (errno));
+				goto out;
+			}
+
+			if (ioctl (fd, VIDIOC_QUERYCAP, &v2cap) == 0) {
+				libhal_changeset_set_property_string (cset,
+								      "video4linux.device", device_file);
+				libhal_changeset_set_property_string (cset,
+								      "info.category", "video4linux");
+				libhal_changeset_set_property_string (cset,
+								      "video4linux.version", "2");
+
+				libhal_changeset_set_property_string (cset,
+								      "info.product", (const char *)v2cap.card);
+
+				libhal_device_add_capability (hal_ctx, hal_udi, "video4linux", NULL);
+				if ((v2cap.capabilities & V4L2_CAP_VIDEO_CAPTURE) > 0) {
+					libhal_device_add_capability (hal_ctx, hal_udi, "video4linux.video_capture", NULL);
+				} if ((v2cap.capabilities & V4L2_CAP_VIDEO_OUTPUT) > 0) {
+					libhal_device_add_capability (hal_ctx, hal_udi, "video4linux.video_output", NULL);
+				} if ((v2cap.capabilities & V4L2_CAP_VIDEO_OVERLAY) > 0) {
+					libhal_device_add_capability (hal_ctx, hal_udi, "video4linux.video_overlay", NULL);
+				} if ((v2cap.capabilities & V4L2_CAP_AUDIO) > 0) {
+					libhal_device_add_capability (hal_ctx, hal_udi, "video4linux.audio", NULL);
+				} if ((v2cap.capabilities & V4L2_CAP_TUNER) > 0) {
+					libhal_device_add_capability (hal_ctx, hal_udi, "video4linux.tuner", NULL);
+				} if ((v2cap.capabilities & V4L2_CAP_RADIO) > 0) {
+					libhal_device_add_capability (hal_ctx, hal_udi, "video4linux.radio", NULL);
+				}
+			} else {
+				h_info (("ioctl VIDIOC_QUERYCAP failed\n"));
+
+				if (ioctl (fd, VIDIOCGCAP, &v1cap) == 0) {
+					libhal_changeset_set_property_string (cset,
+									      "video4linux.device", device_file);
+					libhal_changeset_set_property_string (cset,
+									      "info.category", "video4linux");
+					libhal_changeset_set_property_string (cset,
+									      "video4linux.version", "1");
+
+					libhal_changeset_set_property_string (cset,
+									      "info.product", v1cap.name);
+
+					libhal_device_add_capability (hal_ctx, hal_udi, "video4linux", NULL);
+					if ((v1cap.type & VID_TYPE_CAPTURE) > 0) {
+						libhal_device_add_capability (hal_ctx, hal_udi, "video4linux.video_capture", NULL);
+					} if ((v1cap.type & VID_TYPE_OVERLAY) > 0) {
+						libhal_device_add_capability (hal_ctx, hal_udi, "video4linux.video_overlay", NULL);
+					} if (v1cap.audios > 0) {
+						libhal_device_add_capability (hal_ctx, hal_udi, "video4linux.audio", NULL);
+					} if ((v1cap.type & VID_TYPE_TUNER) > 0) {
+						libhal_device_add_capability (hal_ctx, hal_udi, "video4linux.tuner", NULL);
+					}
+				} else {
+					h_info (("ioctl VIDIOCGCAP failed; not a v4l device\n"));
+				}
+			}
+
+			libhal_device_commit_changeset (hal_ctx, cset, NULL);
+			libhal_device_free_changeset (cset);
+
+			close (fd);
+		} else if (!strncmp(device_file, "/dev/dvb/adapter", sizeof "/dev/dvb/adapter" - 1)) {
+			char *dvbudi;
+			new_dev_t new_dev;
+
+			if (asprintf(&dvbudi, "%s_dvb_%d", hal_udi, dvbindex++) == -1) {
+				perror("asprintf");
+				goto out;
+			}
+			new_dev.udi = strdup(dvbudi);
+			int dev_exists = libhal_device_exists(hal_ctx, dvbudi, NULL);
+
+			if (dev_exists) {
+				new_dev.real_udi = strdup(new_dev.udi);
+			} else {
+				new_dev.real_udi = libhal_new_device(hal_ctx, &error);
+
+				if (!new_dev.real_udi) {
+					fprintf(stderr, "%s: %s\n", error.name, error.message);
+					LIBHAL_FREE_DBUS_ERROR (&error);
+					free(new_dev.real_udi);
+
+					ret = 22;
+					goto out;
+				}
+
+				//printf("tmp udi: %s\n", new_dev.real_udi);
+			}
+
+			cset = libhal_device_new_changeset (new_dev.real_udi);
+
+			h_info ("Doing add-dvb-hal for %s (udi=%s)\n", device_file, new_dev.udi);
+			libhal_changeset_set_property_string (cset,
+							      "dvb.device", device_file);
+			libhal_changeset_set_property_string (cset,
+							      "info.category", "dvb");
+			libhal_changeset_set_property_string (cset,
+							      "info.parent", hal_udi);
+			libhal_changeset_set_property_string (cset,
+							      "info.product", "DVB Device");
+			libhal_changeset_set_property_string (cset,
+							      "info.subsystem", "dvb");
+			libhal_device_add_capability (hal_ctx, new_dev.real_udi, "dvb", NULL);
+
+			libhal_device_commit_changeset (hal_ctx, cset, NULL);
+			libhal_device_free_changeset (cset);
+
+			if (!dev_exists &&
+			    !libhal_device_commit_to_gdl(hal_ctx, new_dev.real_udi, new_dev.udi, &error)) {
+				fprintf(stderr, "%s: %s\n", error.name, error.message);
+				LIBHAL_FREE_DBUS_ERROR (&error);
+				free(new_dev.real_udi);
+
+				ret = 23;
+				goto out;
+			}
+		} else {
+			printf("Unhandled device %s\n", device_file);
+		}
+	}
+
+	ret = 0;
+
+out:
+	if (fd >= 0)
+		close (fd);
+
+	return ret;
+}



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?20100826183543.GA39319>