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

Re: More on hotplug issue w/HAL



On Fri, 2004-01-09 at 18:10, David Zeuthen wrote:

> Basically, since they are class devices they should just merge
> properties into the PCI device (the silicon) they belong to. This would
> amount to adding the capability i2c_adapter to that device and setting
> the property i2c_adaptor.name to the name we get from sysfs.

I think I've now got this implemented in the attached class device
files.  

> This is completely analogue to what the properties merged into the PCI
> device being a USB controller, see
> 
>  http://freedesktop.org/~david/hal-pci-usb.png
> 
> note that this PCI device got the capability serial_controller.usb. This
> would conclude handling of i2c_adapter devices. You would probably put
> this in files linux_class_i2c_adaptor.[ch]
> 
> In addition, we should check for i2c devices (which are physical
> devices) and attach them to the proper parent i2c_adaptor. Do you have
> any i2c devices in your sysfs tree? I don't have any :-/ (need to find
> my tv tuner card). This could go in existing files linux_i2c.[ch].

I had to add a special case to visit_device that adds the appropriate
i2c class device when it finds it.  I couldn't figure out any other way
to ensure that the i2c adapters were added to the tree before their i2c
subdevices were scanned.  USB and PCI have it easy - the bridges for
those devices are on the same bus as the subdevices.  

The attached patch works without any special kernel patching.  Let me
know how it looks.

Thanks,
Matt

-- 
Matthew Mastracci <matt aclaro com>
/***************************************************************************
 * CVSID: $Id$
 *
 * linux_class_i2c_adapter.c : I2C 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 "../logger.h"
#include "../device_store.h"
#include "linux_class_i2c_adapter.h"

/**
 * @defgroup HalDaemonLinuxI2cAdapter I2C adapter class
 * @ingroup HalDaemonLinux
 * @brief I2C adapter class
 * @{
 */


/** This function will compute the device uid based on other properties
 *  of the device. For I2C adapters 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* i2c_adapter_compute_udi(HalDevice* d, int append_num)
{
    const char* format;
    static char buf[256];

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

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


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

/** Visitor function for I2C adapter.
 *
 *  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_i2c_adapter(const char* path, 
                                    struct sysfs_class_device* class_device)
{
    HalDevice* d;
    struct sysfs_attribute* cur;
    char* parent_sysfs_path;
	char* product_name;
    char attr_name[SYSFS_NAME_LEN];
    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(("i2c_adapter: 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", "i2c_adapter");
    ds_property_set_string(d, "linux.sysfs_path", path);
    ds_property_set_string(d, "linux.sysfs_path_device", 
                           class_device->sysdevice->path);

    /* Sets last_elem to i2c-2 in path=/sys/class/i2c-adapter/i2c-2 */
    last_elem = get_last_element(path);
    sscanf(last_elem, "i2c-%d", &adapter_num);
    ds_property_set_int(d, "i2c_adapter.adapter", adapter_num);

    /* guestimate product name */
    dlist_for_each_data(sysfs_get_device_attributes(class_device->sysdevice), 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;
    }

    ds_property_set_string(d, "info.product", "I2C Adapter Interface");
    if ( product_name==NULL )
        ds_property_set_string(d, "i2c_adapter.name", "I2C Adapter Interface");
    else
        ds_property_set_string(d, "i2c_adapter.name", product_name);

    parent_sysfs_path = get_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_i2c_adapter_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_i2c_adapter_got_parent(HalDevice* parent, 
                                                      void* data1, void* data2)
{
    char* new_udi = NULL;
    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 I2C adapter device!"));
        ds_device_destroy(d);
        return;
    }

    /* Add the i2c_adapter capability to our parent device */
	ds_add_capability(parent, "i2c_adapter");

    /* 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, i2c_adapter_compute_udi, "i2c_adapter");
    if( new_udi!=NULL )
    {
        new_d = ds_device_find(new_udi);
        if( new_d!=NULL )
        {
            ds_gdl_add(new_d);
        }
    }
}


/** Init function for I2C adapter class handling
 *
 */
void linux_class_i2c_adapter_init()
{
}

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

/** Shutdown function for I2C adapter class handling
 *
 */
void linux_class_i2c_adapter_shutdown()
{
}

/** @} */
/***************************************************************************
 * CVSID: $Id$
 *
 * linux_class_i2c_adapter.h : I2C 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_I2C_ADAPTER_H
#define LINUX_CLASS_I2C_ADAPTER_H

#include "linux_common.h"

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


void linux_class_i2c_adapter_init();
void linux_class_i2c_adapter_detection_done();
void linux_class_i2c_adapter_shutdown();

#endif /* LINUX_CLASS_I2C_ADAPTER_H */
Index: hald/Makefile.am
===================================================================
RCS file: /cvs/hal/hal/hald/Makefile.am,v
retrieving revision 1.8
diff -u -r1.8 Makefile.am
--- hald/Makefile.am	2 Jan 2004 12:15:52 -0000	1.8
+++ hald/Makefile.am	10 Jan 2004 18:47:06 -0000
@@ -22,11 +22,13 @@
                                         linux/linux_osspec.c            \
         linux/linux_common.h            linux/linux_common.c            \
         linux/linux_pci.h               linux/linux_pci.c               \
+        linux/linux_i2c.h               linux/linux_i2c.c               \
         linux/linux_usb.h               linux/linux_usb.c               \
         linux/linux_ide.h               linux/linux_ide.c               \
         linux/linux_class_block.h       linux/linux_class_block.c       \
         linux/linux_class_scsi.h        linux/linux_class_scsi.c        \
         linux/linux_class_net.h         linux/linux_class_net.c         \
+        linux/linux_class_i2c_adapter.h linux/linux_class_i2c_adapter.c \
         linux/linux_class_input.h       linux/linux_class_input.c
 
 hald_SOURCES +=                                                         \
Index: hald/linux/linux_osspec.c
===================================================================
RCS file: /cvs/hal/hal/hald/linux/linux_osspec.c,v
retrieving revision 1.10
diff -u -r1.10 linux_osspec.c
--- hald/linux/linux_osspec.c	9 Jan 2004 23:22:58 -0000	1.10
+++ hald/linux/linux_osspec.c	10 Jan 2004 18:47:07 -0000
@@ -48,6 +48,7 @@
 #include "linux_ide.h"
 #include "linux_class_block.h"
 #include "linux_class_scsi.h"
+#include "linux_class_i2c_adapter.h"
 #include "linux_class_net.h"
 #include "linux_class_input.h"
 
@@ -100,6 +101,8 @@
         visit_class_device_scsi_host(path, class_device);
     else if( strcmp(class_device->classname, "scsi_device")==0 )
         visit_class_device_scsi_device(path, class_device);
+    else if( strcmp(class_device->classname, "i2c-adapter")==0 )
+        visit_class_device_i2c_adapter(path, class_device);
     else if( strcmp(class_device->classname, "block")==0 )
         visit_class_device_block(path, class_device);
     else if( strcmp(class_device->classname, "net")==0 )
@@ -170,6 +173,8 @@
 {
     struct sysfs_device* device;
     struct sysfs_directory* subdir;
+    struct sysfs_class* cls;
+    struct sysfs_class_device* class_device;
 
     device = sysfs_open_device(path);
     if( device==NULL )
@@ -192,12 +197,27 @@
         /** @todo This is a hack; is there such a thing as an ide_host? */
         else if( strncmp(device->bus_id, "ide", 3)==0 )
             visit_device_ide_host(path, device);
-	/* Hmm only works on some boxes?? So we have to do hack below
         else if( strcmp(device->bus, "i2c")==0 )
-	    visit_device_i2c(path, device);*/
-        else if( strncmp(device->bus_id, "i2c", 3)==0 )
-            visit_device_i2c(path, device); 
-        else 
+            visit_device_i2c(path, device);
+		else if( strncmp(device->bus_id, "i2c", 3)==0 )
+        {
+            /* We need to add the i2c-adapter class devices here, otherwise the I2C
+               devices have nowhere to go */
+            cls = sysfs_open_class("i2c-adapter");
+            if (cls != NULL)
+            {
+                if( cls->devices!=NULL )
+                {
+                    dlist_for_each_data(cls->devices, class_device, struct sysfs_class_device)
+                    {
+                       printf("device->bus_id = %s, cls->name = %s\n", device->bus_id, class_device->name);
+                       if ( strcmp(device->bus_id, class_device->name) == 0 )
+                            visit_class_device_i2c_adapter(path, class_device);
+                    }
+                }
+                sysfs_close_class(cls);
+            }
+        }
         {
             /*printf("bus=%s path=%s\n", device->bus, path);*/
         }
@@ -412,7 +432,8 @@
 
     if( sysfs_devpath[0]!='\0' && 
         (strcmp(subsystem, "usb")==0 ||
-         strcmp(subsystem, "pci")==0) )
+         strcmp(subsystem, "pci")==0 ||
+         strcmp(subsystem, "i2c")==0))
     {
 
         if( is_add )
@@ -461,7 +482,8 @@
              (strcmp(subsystem, "net")==0 ||
               strcmp(subsystem, "block")==0 ||
               strcmp(subsystem, "scsi_host")==0 ||
-              strcmp(subsystem, "scsi_device")==0) )
+              strcmp(subsystem, "scsi_device")==0 ||
+              strcmp(subsystem, "i2c-adapter")==0) )
     {
         if( is_add )
         {
Index: tools/device-manager/Const.py.in
===================================================================
RCS file: /cvs/hal/hal/tools/device-manager/Const.py.in,v
retrieving revision 1.3
diff -u -r1.3 Const.py.in
--- tools/device-manager/Const.py.in	23 Dec 2003 22:29:59 -0000	1.3
+++ tools/device-manager/Const.py.in	10 Jan 2004 18:47:07 -0000
@@ -19,6 +19,8 @@
 BUS_NAMES = {"usb"         : "USB",
              "usbif"       : "USB Interface",
              "pci"         : "PCI",
+             "i2c"         : "I2C",
+             "i2c_adapter" : "I2C Adapter",
              "scsi_host"   : "SCSI Host",
              "scsi_device" : "SCSI",
              "block"       : "Block",

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