[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]

[patch] Video4Linux HAL class



The attached patch + new files adds virtual video4linux class devices to
the HAL tree (and fixes a small bug with the i2c adapters not being
removed properly on hotplug remove).  The V4L devices add the following
multimedia capabilities to their parent device (ie: a bttv-based tv
capture card):

V4L Device | Capability       | Name
radio      | multimedia.radio | Video4Linux Radio Interface
video      | multimedia.vidio | Video4Linux Video Input Interface
vbi        | multimedia.vbi   | Video4Linux VBI Interface
vout       | multimedia.vout  | Video4Linux Video Output Interface

Note that multimedia.vout is different than video.* - v4l video out
devices are accessed through the v4l API.  

-- 
Matthew Mastracci <matt aclaro com>
Index: hald/Makefile.am
===================================================================
RCS file: /cvs/hal/hal/hald/Makefile.am,v
retrieving revision 1.10
diff -r1.10 Makefile.am
31a32
>         linux/linux_class_v4l.h         linux/linux_class_v4l.c         \
Index: hald/linux/linux_osspec.c
===================================================================
RCS file: /cvs/hal/hal/hald/linux/linux_osspec.c,v
retrieving revision 1.11
diff -r1.11 linux_osspec.c
51a52
> #include "linux_class_v4l.h"
105a107,108
>     else if( strcmp(class_device->classname, "video4linux")==0 )
>         visit_class_device_v4l(path, class_device);
217c220
<                             visit_class_device_i2c_adapter(path, class_device);
---
>                             visit_class_device_i2c_adapter(class_device->path, class_device);
265a269
>     linux_class_v4l_init();
317a322,324
>     /* visit class devices in /sys/class/video4linux */
>     visit_class("video4linux", TRUE);
> 
488c495,496
<               strcmp(subsystem, "i2c-adapter")==0) )
---
>               strcmp(subsystem, "i2c-adapter")==0 ||
>               strcmp(subsystem, "video4linux")==0 ))
Index: tools/device-manager/Const.py.in
===================================================================
RCS file: /cvs/hal/hal/tools/device-manager/Const.py.in,v
retrieving revision 1.3
diff -r1.3 Const.py.in
21a22,24
>              "i2c"         : "I2C",
>              "i2c_adapter" : "I2C Adapter",
>              "video4linux" : "Video4Linux",
/***************************************************************************
 * CVSID: $Id: linux_class_v4l.c,v 1.1 2004/01/12 23:30:36 david Exp $
 *
 * linux_class_v4l.c : V4L functions for sysfs-agent on Linux 2.6
 *
 * Copyright (C) 2003 David Zeuthen, <david fubar dk>
 *
 * Licensed under the Academic Free License version 2.0
 *
 * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 **************************************************************************/

#ifdef HAVE_CONFIG_H
#  include <config.h>
#endif

#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <getopt.h>
#include <assert.h>
#include <unistd.h>
#include <stdarg.h>

#include <glib.h>

#include "../logger.h"
#include "../device_store.h"
#include "linux_class_v4l.h"

/**
 * @defgroup HalDaemonLinuxV4lAdapter V4L adapter class
 * @ingroup HalDaemonLinux
 * @brief V4L class
 * @{
 */

/** udev root directory, e.g. "/udev/" */
static char udev_root[256];

/** This function will compute the device uid based on other properties
 *  of the device. For V4L devices it is the adapter number.
 *
 *  @param  d                   HalDevice object
 *  @param  append_num          Number to append to name if not -1
 *  @return                     New unique device id; only good until the
 *                              next invocation of this function
 */
static char* v4l_compute_udi(HalDevice* d, int append_num)
{
    const char* format;
    static char buf[256];

    if( append_num==-1 )
        format = "/org/freedesktop/Hal/devices/video4linux_%s_%d";
    else
        format = "/org/freedesktop/Hal/devices/video4linux_%s_%d-%d";

    snprintf(buf, 256, format, 
             ds_property_get_string(d, "video4linux.class"),
             ds_property_get_int(d, "video4linux.adapter"),
             append_num);
    
    return buf;
}


/* fwd decl */
static void visit_class_device_v4l_got_parent(HalDevice* parent, 
                                              void* data1, void* data2);

/** Visitor function for V4L devices.
 *
 *  This function parses the attributes present and creates a new HAL
 *  device based on this information.
 *
 *  @param  path                Sysfs-path for device
 *  @param  device              libsysfs object for device
 */
void visit_class_device_v4l(const char* path, 
                            struct sysfs_class_device* class_device)
{
    HalDevice* d;
    struct sysfs_attribute* cur;
    char* parent_sysfs_path;
	char* product_name = NULL;
    char attr_name[SYSFS_NAME_LEN];
    char v4l_class[32];
    const char* last_elem;
    int adapter_num;
    int len;
    int i;

    if( class_device->sysdevice==NULL )
    {
        HAL_INFO(("Skipping virtual class device at path %s\n", path));
        return;
    }

    HAL_INFO(("v4l: sysdevice_path=%s, path=%s\n", class_device->sysdevice->path, path));

    /** @todo: see if we already got this device */

    d = ds_device_new();
    ds_property_set_string(d, "info.bus", "video4linux");
    ds_property_set_string(d, "linux.sysfs_path", path);
    ds_property_set_string(d, "linux.sysfs_path_device", path);

    /* Sets last_elem to radio0 in path=/sys/class/video4linux/radio0 */
    last_elem = get_last_element(path);
    sscanf(last_elem, "%32[a-z]%d", v4l_class, &adapter_num);
    ds_property_set_int(d, "video4linux.adapter", adapter_num);
    ds_property_set_string(d, "video4linux.class", v4l_class);

    /* guestimate product name */
    dlist_for_each_data(sysfs_get_classdev_attributes(class_device), cur,
                        struct sysfs_attribute)
    {       
        if( sysfs_get_name_from_path(cur->path, 
                                     attr_name, SYSFS_NAME_LEN) != 0 )
            continue;
        
        /* strip whitespace */
        len = strlen(cur->value);
        for(i=len-1; isspace(cur->value[i]); --i)
            cur->value[i] = '\0';
        
        /*printf("attr_name=%s -> '%s'\n", attr_name, cur->value);*/
        
        if( strcmp(attr_name, "name")==0 )
           product_name = cur->value;
    }

    if ( product_name==NULL )
        ds_property_set_string(d, "video4linux.name", "Video4Linux Interface");
    else
        ds_property_set_string(d, "video4linux.name", product_name);

    /* More info available from http://bytesex.org/v4l/spec-single/v4l2.html */
    if (strcmp("radio", v4l_class) == 0)
        ds_property_set_string(d, "info.product", "Video4Linux Radio Interface");
    else if (strcmp("video", v4l_class) == 0)
        ds_property_set_string(d, "info.product", "Video4Linux Video Input Interface");
    else if (strcmp("vout", v4l_class) == 0)
        ds_property_set_string(d, "info.product", "Video4Linux Video Output Interface");
    else if (strcmp("vbi", v4l_class) == 0)
        ds_property_set_string(d, "info.product", "Video4Linux VBI Interface");
    else 
        ds_property_set_string(d, "info.product", "Video4Linux Interface");

    /* Ask udev about the device file if we are probing.. otherwise we'll just
     * receive a dbus message from udev later */
    if( is_probing )
    {
        int i;
        int sysfs_mount_path_len;
        char sysfs_path_trunc[SYSFS_NAME_LEN];
        char* udev_argv[4] = {"/sbin/udev", "-q", 
                              sysfs_path_trunc, NULL};
        char* udev_stdout;
        char* udev_stderr;
        int udev_exitcode;
        char dev_file[256];

        /* compute truncated sysfs path */
        sysfs_mount_path_len = strlen(sysfs_mount_path);
        if( strlen(path)>sysfs_mount_path_len )
        {
            strncpy(sysfs_path_trunc, path + sysfs_mount_path_len, 
                    SYSFS_NAME_LEN);
        }
        HAL_INFO(("*** sysfs_path_trunc = '%s'", sysfs_path_trunc));
        
        /* Now invoke udev */
        if( g_spawn_sync("/",
                         udev_argv,
                         NULL,
                         0,
                         NULL,
                         NULL,
                         &udev_stdout,
                         &udev_stderr,
                         &udev_exitcode,
			 NULL)!=TRUE )
        {
            HAL_ERROR(("Couldn't invoke /sbin/udev"));
            goto error;
        }
        
        if( udev_exitcode!=0 )
        {
            HAL_ERROR(("/sbin/udev returned %d", udev_exitcode));
            goto error;
        }
        
        /* sanitize string returned by udev */
        for(i=0; udev_stdout[i]!=0; i++)
        {
            if( udev_stdout[i]=='\r' || udev_stdout[i]=='\n' )
            {
                udev_stdout[i]=0;
                break;
            }
        }

        strncpy(dev_file, udev_root, 256);
        strncat(dev_file, udev_stdout, 256);
        
        HAL_INFO(("device file = '%s'", dev_file));
        
        ds_property_set_string(d, "video4linux.device", dev_file);
        
        /** @todo FIXME free udev_stdout, udev_stderr? */        
    }
    else
    {
error:
        ds_property_set_string(d, "video4linux.device", "");
    }
 
    parent_sysfs_path = class_device->sysdevice->path;

    /* Find parent; this happens asynchronously as our parent might
     * be added later. If we are probing this can't happen so the
     * timeout is set to zero in that event..
     */
    ds_device_async_find_by_key_value_string(
        "linux.sysfs_path_device",
        parent_sysfs_path, 
        TRUE,
        visit_class_device_v4l_got_parent,
        (void*) d, NULL, 
        is_probing ? 0 :
        HAL_LINUX_HOTPLUG_TIMEOUT);

    free(parent_sysfs_path);
}

/** Callback when the parent is found or if there is no parent.. This is
 *  where we get added to the GDL..
 *
 *  @param  parent              Async Return value from the find call
 *  @param  data1               User data
 *  @param  data2               User data
 */
static void visit_class_device_v4l_got_parent(HalDevice* parent, 
                                              void* data1, void* data2)
{
    char v4l_capability[SYSFS_NAME_LEN];
    char* new_udi = NULL;
    char* parent_driver;
    HalDevice* new_d = NULL;
    HalDevice* d = (HalDevice*) data1;

    /*printf("parent=0x%08x\n", parent);*/

    if( parent!=NULL )
    {
        ds_property_set_string(d, "info.parent", parent->udi);
        find_and_set_physical_device(d);
        ds_property_set_bool(d, "info.virtual", TRUE);
    }
    else
    {
        HAL_ERROR(("No parent for V4L device!"));
        ds_device_destroy(d);
        return;
    }

    /* Add the appropriate v4l capability to our parent device */
    snprintf(v4l_capability, SYSFS_NAME_LEN, "multimedia.%s", ds_property_get_string(d, "video4linux.class"));
	ds_add_capability(parent, v4l_capability);

    /* Use the driver from our parent */
    parent_driver = ds_property_get_string(parent, "linux.driver");
    if ( parent_driver != NULL )
        ds_property_set_string(d, "linux.driver", parent_driver);

    /* Compute a proper UDI (unique device id) and try to locate a persistent
     * unplugged device or simple add this new device...
     */
    new_udi = rename_and_merge(d, v4l_compute_udi, "video4linux");
    if( new_udi!=NULL )
    {
        new_d = ds_device_find(new_udi);
        if( new_d!=NULL )
        {
            ds_gdl_add(new_d);
        }
    }
}

/** Find udev root directory (e.g. '/udev/') by invoking '/sbin/udev -r'.
 *  If this fails, we default to /udev/.
 *
 */
static void get_udev_root()
{
    int i;
    char* udev_argv[3] = {"/sbin/udev", "-r", NULL};
    char* udev_stdout;
    char* udev_stderr;
    int udev_exitcode;

    strncpy(udev_root, "/udev/", 256);

    /* Invoke udev */
    if( g_spawn_sync("/",
                     udev_argv,
                     NULL,
                     0,
                     NULL,
                     NULL,
                     &udev_stdout,
                     &udev_stderr,
                     &udev_exitcode,
		     NULL)!=TRUE )
    {
        HAL_ERROR(("Couldn't invoke /sbin/udev -r"));
        return;
    }
    
    if( udev_exitcode!=0 )
    {
        HAL_ERROR(("/sbin/udev -r returned %d", udev_exitcode));
        return;
    }
    
    /* sanitize string returned by udev */
    for(i=0; udev_stdout[i]!=0; i++)
    {
        if( udev_stdout[i]=='\r' || udev_stdout[i]=='\n' )
        {
            udev_stdout[i]=0;
            break;
        }
    }
    
    strncpy(udev_root, udev_stdout, 256);
        
    HAL_INFO(("udev root = '%s'", udev_root));
        
    /** @todo FIXME free udev_stdout, udev_stderr? */        
}

/** Init function for V4L adapter class handling
 *
 */
void linux_class_v4l_init()
{
    get_udev_root();
}

/** This function is called when all device detection on startup is done
 *  in order to perform optional batch processing on devices
 *
 */
void linux_class_v4l_detection_done()
{
}

/** Shutdown function for V4L adapter class handling
 *
 */
void linux_class_v4l_shutdown()
{
}

/** @} */
/***************************************************************************
 * CVSID: $Id: linux_class_v4l.h,v 1.1 2004/01/12 23:30:36 david Exp $
 *
 * linux_class_v4l.h : V4L device handling on Linux 2.6
 *
 * Copyright (C) 2003 David Zeuthen, <david fubar dk>
 *
 * Licensed under the Academic Free License version 2.0
 *
 * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 **************************************************************************/

#ifndef LINUX_CLASS_V4L_ADAPTER_H
#define LINUX_CLASS_V4L_ADAPTER_H

#include "linux_common.h"

void visit_class_device_v4l(const char* path, 
                            struct sysfs_class_device *class_device);


void linux_class_v4l_init();
void linux_class_v4l_detection_done();
void linux_class_v4l_shutdown();

#endif /* LINUX_CLASS_V4L_ADAPTER_H */

[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]