Main Page | Modules | Data Structures | File List | Data Fields | Related Pages

linux_osspec.c

00001 /***************************************************************************
00002  * CVSID: $Id: linux_osspec.c,v 1.14 2004/01/19 23:30:12 david Exp $
00003  *
00004  * probe.c : Handling hardware of on Linux, using kernel 2.6
00005  *
00006  * Copyright (C) 2003 David Zeuthen, <david@fubar.dk>
00007  *
00008  * Licensed under the Academic Free License version 2.0
00009  *
00010  * This program is free software; you can redistribute it and/or modify
00011  * it under the terms of the GNU General Public License as published by
00012  * the Free Software Foundation; either version 2 of the License, or
00013  * (at your option) any later version.
00014  *
00015  * This program is distributed in the hope that it will be useful,
00016  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00017  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00018  * GNU General Public License for more details.
00019  *
00020  * You should have received a copy of the GNU General Public License
00021  * along with this program; if not, write to the Free Software
00022  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00023  *
00024  **************************************************************************/
00025 
00026 #ifdef HAVE_CONFIG_H
00027 #  include <config.h>
00028 #endif
00029 
00030 #include <ctype.h>
00031 #include <stdio.h>
00032 #include <stdlib.h>
00033 #include <string.h>
00034 #include <getopt.h>
00035 #include <assert.h>
00036 #include <unistd.h>
00037 #include <stdarg.h>
00038 
00039 #include <dbus/dbus.h>
00040 
00041 #include "../osspec.h"
00042 #include "../logger.h"
00043 
00044 #include "linux_common.h"
00045 #include "linux_pci.h"
00046 #include "linux_i2c.h"
00047 #include "linux_usb.h"
00048 #include "linux_ide.h"
00049 #include "linux_class_block.h"
00050 #include "linux_class_scsi.h"
00051 #include "linux_class_i2c_adapter.h"
00052 #include "linux_class_v4l.h"
00053 #include "linux_class_net.h"
00054 #include "linux_class_input.h"
00055 
00056 #include "libsysfs/libsysfs.h"
00057 
00067 char sysfs_mount_path[SYSFS_PATH_MAX];
00068 
00069 
00070 
00085 static void visit_class_device(const char* path, dbus_bool_t visit_children)
00086 {
00087     struct sysfs_class_device* class_device;
00088     struct sysfs_directory* subdir;
00089 
00090     class_device = sysfs_open_class_device(path);
00091     if( class_device==NULL )
00092         DIE(("Coulnd't get sysfs class device object for path %s", path));
00093 
00094 /*
00095     printf("    visit_class_device classname=%s name=%s path=%s\n",
00096            class_device->classname,
00097            class_device->name,
00098            class_device->path);
00099 */
00100 
00101     if( strcmp(class_device->classname, "scsi_host")==0 )
00102         visit_class_device_scsi_host(path, class_device);
00103     else if( strcmp(class_device->classname, "scsi_device")==0 )
00104         visit_class_device_scsi_device(path, class_device);
00105     else if( strcmp(class_device->classname, "i2c-adapter")==0 )
00106         visit_class_device_i2c_adapter(path, class_device);
00107     else if( strcmp(class_device->classname, "video4linux")==0 )
00108         visit_class_device_v4l(path, class_device);
00109     else if( strcmp(class_device->classname, "block")==0 )
00110         visit_class_device_block(path, class_device);
00111     else if( strcmp(class_device->classname, "net")==0 )
00112         visit_class_device_net(path, class_device);
00113     /* Visit children */
00114     if( visit_children && class_device->directory!=NULL &&
00115         class_device->directory->subdirs!=NULL )
00116     {
00117         dlist_for_each_data(class_device->directory->subdirs, subdir, 
00118                             struct sysfs_directory)
00119         {
00120             char newpath[SYSFS_PATH_MAX];
00121             snprintf(newpath, SYSFS_PATH_MAX, "%s/%s", path, subdir->name);
00122             visit_class_device(newpath, TRUE);
00123         }
00124     }
00125 
00126     sysfs_close_class_device(class_device);
00127 }
00128 
00139 static void visit_class(const char* class_name, dbus_bool_t visit_children)
00140 {
00141     struct sysfs_class* cls = NULL;
00142     struct sysfs_class_device* cur = NULL;
00143 
00144     cls = sysfs_open_class(class_name);
00145     if( cls==NULL )
00146     {
00147         HAL_ERROR(("Error opening class %s\n", class_name));
00148         return;
00149     }
00150 
00151     if( cls->devices!=NULL )
00152     {
00153         dlist_for_each_data(cls->devices, cur, struct sysfs_class_device)
00154         {
00155             visit_class_device(cur->path, visit_children);
00156         }
00157     }
00158     
00159     sysfs_close_class(cls);
00160 }
00161 
00175 static void visit_device(const char* path, dbus_bool_t visit_children)
00176 {
00177     struct sysfs_device* device;
00178     struct sysfs_directory* subdir;
00179     struct sysfs_class* cls;
00180     struct sysfs_class_device* class_device;
00181 
00182     device = sysfs_open_device(path);
00183     if( device==NULL )
00184         DIE(("Coulnd't get sysfs device object for path %s", path));
00185 
00186 /*
00187     printf("############\n");
00188     printf("############    %s  busid=%s\n", device->bus, device->bus_id);
00189     printf("############\n");
00190 */
00191 
00192     if( device->bus!=NULL )
00193     {
00194         if( strcmp(device->bus, "pci")==0 )
00195             visit_device_pci(path, device);
00196         else if( strcmp(device->bus, "usb")==0 )
00197             visit_device_usb(path, device);
00198 /*
00199         else if( strcmp(device->bus, "ieee1394")==0 )
00200             visit_device_ieee1394(path, device);
00201 */
00202         else if( strcmp(device->bus, "ide")==0 )
00203             visit_device_ide(path, device);
00205         else if( strncmp(device->bus_id, "ide", 3)==0 )
00206             visit_device_ide_host(path, device);
00207         else if( strcmp(device->bus, "i2c")==0 )
00208             visit_device_i2c(path, device);
00209         else if( strncmp(device->bus_id, "i2c", 3)==0 )
00210         {
00211             /* @todo FIXME we need to add the i2c-adapter class
00212              * devices here, otherwise the I2C devices have nowhere to
00213              * go
00214              */
00215             cls = sysfs_open_class("i2c-adapter");
00216             if (cls != NULL)
00217             {
00218                 if( cls->devices!=NULL )
00219                 {
00220                     dlist_for_each_data(cls->devices, class_device, struct sysfs_class_device)
00221                     {
00222                        printf("device->bus_id = %s, cls->name = %s\n", device->bus_id, class_device->name);
00223                        if ( strcmp(device->bus_id, class_device->name) == 0 )
00224                             visit_class_device_i2c_adapter(class_device->path, class_device);
00225                     }
00226                 }
00227                 sysfs_close_class(cls);
00228             }
00229         }
00230         {
00231             /*printf("bus=%s path=%s\n", device->bus, path);*/
00232         }
00233         /*
00234         */
00235 /*
00236  */
00237     }
00238 
00239     /* Visit children */
00240     if( visit_children && device->directory->subdirs!=NULL )
00241     {
00242         dlist_for_each_data(device->directory->subdirs, subdir, 
00243                             struct sysfs_directory)
00244         {
00245             char newpath[SYSFS_PATH_MAX];
00246             snprintf(newpath, SYSFS_PATH_MAX, "%s/%s", path, subdir->name);
00247             visit_device(newpath, TRUE);
00248         }
00249     }
00250 
00251     sysfs_close_device(device);
00252 }
00253 
00254 
00255 
00256 /* This function is documented in ../osspec.h */
00257 void osspec_init(DBusConnection* dbus_connection)
00258 {
00259     int rc;
00260     DBusError error;
00261 
00262     /* get mount path for sysfs */
00263     rc = sysfs_get_mnt_path(sysfs_mount_path, SYSFS_PATH_MAX);
00264     if( rc!=0 )
00265     {
00266         DIE(("Couldn't get mount path for sysfs"));
00267     }
00268     HAL_INFO(("Mountpoint for sysfs is %s", sysfs_mount_path));
00269 
00270     linux_pci_init();
00271     linux_usb_init();
00272     /*linux_ieee1394_init();*/
00273     linux_ide_init();
00274     linux_class_v4l_init();
00275     linux_class_scsi_init();
00276     linux_class_block_init();
00277     linux_class_net_init();
00278 
00279     /* Add match for signals from udev */
00280     dbus_error_init(&error);
00281     dbus_bus_add_match(dbus_connection, 
00282                        "type='signal',"
00283                        "interface='org.kernel.udev.NodeMonitor',"
00284                        /*"sender='org.kernel.udev'," until D-BUS is fixed*/
00285                        "path='/org/kernel/udev/NodeMonitor'", &error);
00286     if( dbus_error_is_set(&error) )
00287     {
00288         HAL_WARNING(("Cannot subscribe to udev signals, error=%s", 
00289                      error.message));
00290     }
00291 }
00292 
00294 dbus_bool_t is_probing;
00295 
00296 /* This function is documented in ../osspec.h */
00297 void osspec_probe()
00298 {
00299     char path[SYSFS_PATH_MAX];
00300     struct sysfs_directory* current;
00301     struct sysfs_directory* dir;
00302 
00303     is_probing = TRUE;
00304 
00305     /* traverse /sys/devices */
00306     strncpy(path, sysfs_mount_path, SYSFS_PATH_MAX);
00307     strncat(path, SYSFS_DEVICES_DIR, SYSFS_PATH_MAX);
00308 
00309     dir = sysfs_open_directory(path);
00310     if( dir==NULL )
00311     {
00312         DIE(("Error opening sysfs directory at %s\n", path));
00313     }
00314     if( sysfs_read_directory(dir)!=0 )
00315     {
00316         DIE(("Error reading sysfs directory at %s\n", path));
00317     }
00318     if( dir->subdirs!=NULL )
00319     {
00320         dlist_for_each_data(dir->subdirs, current, struct sysfs_directory)
00321         {
00322             visit_device(current->path, TRUE);
00323         }
00324     }
00325     sysfs_close_directory(dir);
00326 
00327     /* visit class devices in /sys/class/video4linux */
00328     visit_class("video4linux", TRUE);
00329 
00330     /* visit class devices in /sys/class/scsi_host */
00331     visit_class("scsi_host", FALSE);
00332 
00333     /* visit class devices in /sys/class/scsi_host */
00334     visit_class("scsi_device", FALSE);
00335 
00336     /* visit all block devices */
00337     visit_class("block", TRUE);
00338 
00339     /* visit all net devices */
00340     visit_class("net", TRUE);
00341 
00342     /* input devices are not yet in sysfs */
00343     linux_class_input_probe();
00344 
00345     is_probing = FALSE;
00346 
00347     /* Notify various device and class types that detection is done, so 
00348      * they can do some (optional) batch processing
00349      */
00350     linux_pci_detection_done();
00351     linux_usb_detection_done();
00352     /*linux_ieee1394_detection_done();*/
00353     linux_ide_detection_done();
00354     linux_class_block_detection_done();
00355     linux_class_input_detection_done();
00356     linux_class_net_detection_done();
00357     linux_class_scsi_detection_done();
00358 }
00359 
00361 #define HOTPLUG_INPUT_MAX 128
00362 
00371 static DBusHandlerResult handle_hotplug(DBusConnection* connection,
00372                                         DBusMessage* message)
00373 {
00374     DBusMessageIter iter;
00375     DBusMessageIter dict_iter;
00376     dbus_bool_t is_add;
00377     char* subsystem;
00378     char input_name[HOTPLUG_INPUT_MAX];
00379     char input_phys[HOTPLUG_INPUT_MAX];
00380     char input_key[HOTPLUG_INPUT_MAX];
00381     int input_ev=0;
00382     int input_rel=0;
00383     int input_abs=0;
00384     int input_led=0;
00385     char sysfs_devpath[SYSFS_PATH_MAX];
00386 
00387     sysfs_devpath[0] = '\0';
00388     input_name[0] = input_phys[0] = input_key[0] = '\0';
00389 
00390     dbus_message_iter_init(message, &iter);
00391 
00392     if( dbus_message_iter_get_arg_type(&iter)!=DBUS_TYPE_STRING )
00393     {
00395         dbus_message_unref(message);
00396         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
00397     }
00398     subsystem = dbus_message_iter_get_string(&iter);
00399 
00400     dbus_message_iter_next(&iter);
00401     dbus_message_iter_init_dict_iterator(&iter, &dict_iter);
00402 
00403     is_add = FALSE;
00404 
00405     for( ;dbus_message_iter_has_next(&dict_iter); 
00406          dbus_message_iter_next(&dict_iter))
00407     {
00408         char* key;
00409         char* value;
00410         
00411         key = dbus_message_iter_get_dict_key(&dict_iter);
00412         value = dbus_message_iter_get_string(&dict_iter);
00413 
00414         /*printf("  key/value = %s |-> %s\n", key, value);*/
00415 
00416         if( strcmp(key, "ACTION")==0 )
00417         {
00418             if( strcmp(value, "add")==0 )
00419             {
00420                 is_add = TRUE;
00421             }
00422         }
00423         else if( strcmp(key, "DEVPATH")==0 )
00424         {
00425             strncpy(sysfs_devpath, sysfs_mount_path, SYSFS_PATH_MAX);
00426             strncat(sysfs_devpath, value, SYSFS_PATH_MAX);
00427         }
00428         else if( strcmp(key, "NAME")==0 )
00429             strncpy(input_name, value, HOTPLUG_INPUT_MAX);
00430         else if( strcmp(key, "PHYS")==0 )
00431             strncpy(input_phys, value, HOTPLUG_INPUT_MAX);
00432         else if( strcmp(key, "KEY")==0 )
00433             strncpy(input_key, value, HOTPLUG_INPUT_MAX);
00434         else if( strcmp(key, "EV")==0 )
00435             input_ev = parse_dec(value);
00436         else if( strcmp(key, "REL")==0 )
00437             input_rel = parse_hex(value);
00438         else if( strcmp(key, "ABS")==0 )
00439             input_abs = parse_hex(value);
00440         else if( strcmp(key, "LED")==0 )
00441             input_led = parse_hex(value);
00442     }
00443 
00444     HAL_INFO(("HotplugEvent %s, subsystem=%s devpath=%s", 
00445               (is_add ? "add" : "remove"), subsystem,
00446               sysfs_devpath[0]!='\0' ? sysfs_devpath : "(none)"));
00447 
00448     if( sysfs_devpath[0]!='\0' && 
00449         (strcmp(subsystem, "usb")==0 ||
00450          strcmp(subsystem, "pci")==0 ||
00451          /*strcmp(subsystem, "ieee1394")==0 ||*/
00452          strcmp(subsystem, "i2c")==0))
00453     {
00454 
00455         if( is_add )
00456         {
00457             HAL_INFO(("Adding device @ sysfspath %s", sysfs_devpath));
00458             visit_device(sysfs_devpath, FALSE);
00459         }
00460         else
00461         {
00462             HalDevice* d;
00463 
00464             d = ds_device_find_by_key_value_string("linux.sysfs_path",
00465                                                    sysfs_devpath, TRUE);
00466             if( d==NULL )
00467             {
00468                 HAL_WARNING(("Couldn't remove device @ %s on hotplug remove",
00469                              sysfs_devpath));
00470             }
00471             else
00472             {
00473                 HAL_INFO(("Removing device @ sysfspath %s, udi %s", 
00474                           sysfs_devpath, d->udi));
00475 
00476                 if( ds_property_exists(d, "info.persistent") &&
00477                     ds_property_get_bool(d, "info.persistent") )
00478                 {
00479                     ds_property_set_bool(d, "info.not_available", TRUE);
00480                     /* Remove enough specific details so we are not found
00481                      * by child devices when being plugged in again.. 
00482                      */
00483                     ds_property_remove(d, "info.parent");
00484                     ds_property_remove(d, "info.physical_device");
00485                     ds_property_remove(d, "linux.sysfs_path");
00486                     ds_property_remove(d, "linux.sysfs_path_device");
00487                     HAL_INFO(("Device %s is persistent, so not removed",
00488                               d->udi));
00489                 }
00490                 else
00491                 {
00492                     ds_device_destroy(d);
00493                 }
00494             }
00495         }
00496     }
00497     else if( sysfs_devpath[0]!='\0' && 
00498              (strcmp(subsystem, "net")==0 ||
00499               strcmp(subsystem, "block")==0 ||
00500               strcmp(subsystem, "scsi_host")==0 ||
00501               strcmp(subsystem, "scsi_device")==0 ||
00502               strcmp(subsystem, "i2c-adapter")==0 ||
00503               strcmp(subsystem, "video4linux")==0 ))
00504     {
00505         if( is_add )
00506         {
00507             HAL_INFO(("Adding classdevice @ sysfspath %s", sysfs_devpath));
00508             visit_class_device(sysfs_devpath, FALSE);
00509         }
00510         else
00511         {
00512             HalDevice* d;
00513             d = ds_device_find_by_key_value_string("linux.sysfs_path",
00514                                                    sysfs_devpath, TRUE);
00515             if( d==NULL )
00516             {
00517                 HAL_WARNING(("Couldn't remove device @ %s on hotplug remove",
00518                              sysfs_devpath));
00519             }
00520             else
00521             {
00522                 if( strcmp(subsystem, "block")==0 )
00523                     linux_class_block_removed(d);
00524 
00525                 HAL_INFO(("Removing classdevice @ sysfspath %s, udi %s", 
00526                           sysfs_devpath, d->udi));
00527                 ds_device_destroy(d);
00528             }
00529         }
00530     }
00531     else if( strcmp(subsystem, "input")==0 )
00532     {
00533         if( is_add )
00534         {
00538             linux_class_input_handle_hotplug_add(input_name, input_phys, 
00539                                                  input_key, 
00540                                                  input_ev, input_rel, 
00541                                                  input_abs, input_led);
00542         }
00543         else
00544         {
00545             /* This block is left intentionally blank, since input class
00546              * devices simply attach to other (physical) devices and when
00547              * they disappear, the input part also goes away
00548              */
00549         }
00550     }
00551 
00552     return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
00553 }
00554 
00555 /* fwd decl */
00556 static void handle_udev_node_created_found_device(HalDevice* d, 
00557                                                   void* data1, void* data2);
00558 
00567 static DBusHandlerResult handle_udev_node_created(DBusConnection* connection,
00568                                                   DBusMessage* message)
00569 {
00570     char* filename;
00571     char* sysfs_path;
00572     char sysfs_dev_path[SYSFS_PATH_MAX];
00573 
00574     if( dbus_message_get_args(message, NULL, 
00575                               DBUS_TYPE_STRING, &filename,
00576                               DBUS_TYPE_STRING, &sysfs_path,
00577                               DBUS_TYPE_INVALID) )
00578     {
00579         strncpy(sysfs_dev_path, sysfs_mount_path, SYSFS_PATH_MAX);
00580         strncat(sysfs_dev_path, sysfs_path, SYSFS_PATH_MAX);
00581         HAL_INFO(("udev NodeCreated: %s %s\n", filename, sysfs_dev_path));
00582         
00583         /* Find block device; this happens asynchronously as our it might
00584          * be added later..
00585          */
00586         ds_device_async_find_by_key_value_string(
00587             "linux.sysfs_path_device",
00588             sysfs_dev_path, 
00589             FALSE, /* note: it doesn't need to be in the GDL */
00590             handle_udev_node_created_found_device,
00591             (void*) filename, NULL, 
00592             HAL_LINUX_HOTPLUG_TIMEOUT);
00593 
00594         /* NOTE NOTE NOTE: we will free filename in async result function */
00595 
00596         dbus_free(sysfs_path);
00597     }
00598 
00599     return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
00600 }
00601 
00608 static void handle_udev_node_created_found_device(HalDevice* d, 
00609                                                   void* data1, void* data2)
00610 {
00611     char* filename = (char*) data1;
00612 
00613     if( d!=NULL )
00614     {
00615         HAL_INFO(("Setting block.device=%s for udi=%s", filename, d->udi));
00616         ds_property_set_string(d, "block.device", filename);
00617         linux_class_block_check_if_ready_to_add(d);
00618     }
00619     else
00620     {
00621         HAL_WARNING(("No HAL device corresponding to device %s", filename));
00622     }
00623 
00624     dbus_free(filename);
00625 }
00626 
00635 DBusHandlerResult osspec_filter_function(DBusConnection* connection,
00636                                          DBusMessage* message,
00637                                          void* user_data)
00638 {
00639 
00640 /*
00641     HAL_INFO(("obj_path=%s interface=%s method=%s", 
00642               dbus_message_get_path(message), 
00643               dbus_message_get_interface(message),
00644               dbus_message_get_member(message)));
00645 */
00646 
00647     if( dbus_message_is_method_call(
00648             message,
00649             "org.freedesktop.Hal.Linux.Hotplug",
00650             "HotplugEvent") &&
00651         strcmp(dbus_message_get_path(message), 
00652                "/org/freedesktop/Hal/Linux/Hotplug")==0 )
00653     {
00654         return handle_hotplug(connection, message);
00655     }
00656     else if( dbus_message_is_signal(message, 
00657                                     "org.kernel.udev.NodeMonitor",
00658                                     "NodeCreated") )
00659     {
00660         return handle_udev_node_created(connection, message);
00661     }
00662     else if( dbus_message_is_signal(message, "org.kernel.udev.NodeMonitor",
00663                                     "NodeDeleted") )
00664     {
00665         /* This is left intentionally blank since this it means that a
00666          * block device is removed and we'll catch that other places
00667          */
00668     }
00669 
00670     return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
00671 }
00672 

Generated on Sat Feb 7 22:11:47 2004 for HAL by doxygen 1.3.5