From owner-freebsd-multimedia@FreeBSD.ORG Thu Aug 26 18:38:03 2010 Return-Path: Delivered-To: freebsd-multimedia@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34]) by hub.freebsd.org (Postfix) with ESMTP id 5475F10656A6; Thu, 26 Aug 2010 18:38:03 +0000 (UTC) (envelope-from nox@jelal.kn-bremen.de) Received: from smtp.kn-bremen.de (gelbbaer.kn-bremen.de [78.46.108.116]) by mx1.freebsd.org (Postfix) with ESMTP id 78F6A8FC17; Thu, 26 Aug 2010 18:38:02 +0000 (UTC) Received: by smtp.kn-bremen.de (Postfix, from userid 10) id 754531E0017B; Thu, 26 Aug 2010 20:38:01 +0200 (CEST) Received: from triton8.kn-bremen.de (noident@localhost [127.0.0.1]) by triton8.kn-bremen.de (8.14.4/8.14.3) with ESMTP id o7QIZhfN039371; Thu, 26 Aug 2010 20:35:43 +0200 (CEST) (envelope-from nox@triton8.kn-bremen.de) Received: (from nox@localhost) by triton8.kn-bremen.de (8.14.4/8.14.3/Submit) id o7QIZhxG039370; Thu, 26 Aug 2010 20:35:43 +0200 (CEST) (envelope-from nox) From: Juergen Lock Date: Thu, 26 Aug 2010 20:35:43 +0200 To: Hans Petter Selasky Message-ID: <20100826183543.GA39319@triton8.kn-bremen.de> References: <4C66C4BC.4040504@janh.de> <4C7435AA.50805@freebsd.org> <20100825214141.GA8685@triton8.kn-bremen.de> <201008252350.05592.hselasky@freebsd.org> MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Disposition: inline In-Reply-To: <201008252350.05592.hselasky@freebsd.org> User-Agent: Mutt/1.5.20 (2009-06-14) Cc: me@janh.de, kde@freebsd.org, Juergen Lock , freebsd-multimedia@freebsd.org, Joe Marcus Clarke , gnome@freebsd.org Subject: Re: kaffeine-1.0 and webcamd based DVB-T? X-BeenThere: freebsd-multimedia@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: Multimedia discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Thu, 26 Aug 2010 18:38:03 -0000 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 < > > >> 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 < > > >> 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 < > > > > > > > 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 +#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 + * + * 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 + * + * 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 +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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; +}