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; +}