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

linux_osspec.c

00001 /***************************************************************************
00002  * CVSID: $Id: linux_osspec.c,v 1.15 2004/03/03 17:56:56 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
00086 visit_class_device (const char *path, dbus_bool_t visit_children)
00087 {
00088     struct sysfs_class_device *class_device;
00089     struct sysfs_directory *subdir;
00090 
00091     class_device = sysfs_open_class_device (path);
00092     if (class_device == NULL)
00093         DIE (("Coulnd't get sysfs class device object for path %s",
00094               path));
00095 
00096 /*
00097     printf("    visit_class_device classname=%s name=%s path=%s\n",
00098            class_device->classname,
00099            class_device->name,
00100            class_device->path);
00101 */
00102 
00103     if (strcmp (class_device->classname, "scsi_host") == 0)
00104         visit_class_device_scsi_host (path, class_device);
00105     else if (strcmp (class_device->classname, "scsi_device") == 0)
00106         visit_class_device_scsi_device (path, class_device);
00107     else if (strcmp (class_device->classname, "i2c-adapter") == 0)
00108         visit_class_device_i2c_adapter (path, class_device);
00109     else if (strcmp (class_device->classname, "video4linux") == 0)
00110         visit_class_device_v4l (path, class_device);
00111     else if (strcmp (class_device->classname, "block") == 0)
00112         visit_class_device_block (path, class_device);
00113     else if (strcmp (class_device->classname, "net") == 0)
00114         visit_class_device_net (path, class_device);
00115     /* Visit children */
00116     if (visit_children && class_device->directory != NULL &&
00117         class_device->directory->subdirs != NULL) {
00118         dlist_for_each_data (class_device->directory->subdirs,
00119                      subdir, struct sysfs_directory) {
00120             char newpath[SYSFS_PATH_MAX];
00121             snprintf (newpath, SYSFS_PATH_MAX, "%s/%s", path,
00122                   subdir->name);
00123             visit_class_device (newpath, TRUE);
00124         }
00125     }
00126 
00127     sysfs_close_class_device (class_device);
00128 }
00129 
00140 static void
00141 visit_class (const char *class_name, dbus_bool_t visit_children)
00142 {
00143     struct sysfs_class *cls = NULL;
00144     struct sysfs_class_device *cur = NULL;
00145 
00146     cls = sysfs_open_class (class_name);
00147     if (cls == NULL) {
00148         HAL_ERROR (("Error opening class %s\n", class_name));
00149         return;
00150     }
00151 
00152     if (cls->devices != NULL) {
00153         dlist_for_each_data (cls->devices, cur,
00154                      struct sysfs_class_device) {
00155             visit_class_device (cur->path, visit_children);
00156         }
00157     }
00158 
00159     sysfs_close_class (cls);
00160 }
00161 
00175 static void
00176 visit_device (const char *path, dbus_bool_t visit_children)
00177 {
00178     struct sysfs_device *device;
00179     struct sysfs_directory *subdir;
00180     struct sysfs_class *cls;
00181     struct sysfs_class_device *class_device;
00182 
00183     device = sysfs_open_device (path);
00184     if (device == NULL)
00185         DIE (("Coulnd't get sysfs device object for path %s",
00186               path));
00187 
00188 /*
00189     printf("############\n");
00190     printf("############    %s  busid=%s\n", device->bus, device->bus_id);
00191     printf("############\n");
00192 */
00193 
00194     if (device->bus != NULL) {
00195         if (strcmp (device->bus, "pci") == 0)
00196             visit_device_pci (path, device);
00197         else if (strcmp (device->bus, "usb") == 0)
00198             visit_device_usb (path, device);
00199 /*
00200         else if( strcmp(device->bus, "ieee1394")==0 )
00201             visit_device_ieee1394(path, device);
00202 */
00203         else if (strcmp (device->bus, "ide") == 0)
00204             visit_device_ide (path, device);
00206         else if (strncmp (device->bus_id, "ide", 3) == 0)
00207             visit_device_ide_host (path, device);
00208         else if (strcmp (device->bus, "i2c") == 0)
00209             visit_device_i2c (path, device);
00210         else if (strncmp (device->bus_id, "i2c", 3) == 0) {
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                 if (cls->devices != NULL) {
00218                     dlist_for_each_data (cls->devices,
00219                                  class_device,
00220                                  struct
00221                                  sysfs_class_device)
00222                     {
00223                         printf
00224                             ("device->bus_id = %s, cls->name = %s\n",
00225                              device->bus_id,
00226                              class_device->name);
00227                         if (strcmp
00228                             (device->bus_id,
00229                              class_device->name) ==
00230                             0)
00231                             visit_class_device_i2c_adapter
00232                                 (class_device->
00233                                  path,
00234                                  class_device);
00235                     }
00236                 }
00237                 sysfs_close_class (cls);
00238             }
00239         }
00240         {
00241             /*printf("bus=%s path=%s\n", device->bus, path); */
00242         }
00243         /*
00244          */
00245 /*
00246  */
00247     }
00248 
00249     /* Visit children */
00250     if (visit_children && device->directory->subdirs != NULL) {
00251         dlist_for_each_data (device->directory->subdirs, subdir,
00252                      struct sysfs_directory) {
00253             char newpath[SYSFS_PATH_MAX];
00254             snprintf (newpath, SYSFS_PATH_MAX, "%s/%s", path,
00255                   subdir->name);
00256             visit_device (newpath, TRUE);
00257         }
00258     }
00259 
00260     sysfs_close_device (device);
00261 }
00262 
00263 
00264 
00265 /* This function is documented in ../osspec.h */
00266 void
00267 osspec_init (DBusConnection * dbus_connection)
00268 {
00269     int rc;
00270     DBusError error;
00271 
00272     /* get mount path for sysfs */
00273     rc = sysfs_get_mnt_path (sysfs_mount_path, SYSFS_PATH_MAX);
00274     if (rc != 0) {
00275         DIE (("Couldn't get mount path for sysfs"));
00276     }
00277     HAL_INFO (("Mountpoint for sysfs is %s", sysfs_mount_path));
00278 
00279     linux_pci_init ();
00280     linux_usb_init ();
00281     /*linux_ieee1394_init(); */
00282     linux_ide_init ();
00283     linux_class_v4l_init ();
00284     linux_class_scsi_init ();
00285     linux_class_block_init ();
00286     linux_class_net_init ();
00287 
00288     /* Add match for signals from udev */
00289     dbus_error_init (&error);
00290     dbus_bus_add_match (dbus_connection,
00291                 "type='signal',"
00292                 "interface='org.kernel.udev.NodeMonitor',"
00293                 /*"sender='org.kernel.udev'," until D-BUS is fixed */
00294                 "path='/org/kernel/udev/NodeMonitor'", &error);
00295     if (dbus_error_is_set (&error)) {
00296         HAL_WARNING (("Cannot subscribe to udev signals, error=%s",
00297                   error.message));
00298     }
00299 }
00300 
00302 dbus_bool_t is_probing;
00303 
00304 /* This function is documented in ../osspec.h */
00305 void
00306 osspec_probe ()
00307 {
00308     char path[SYSFS_PATH_MAX];
00309     struct sysfs_directory *current;
00310     struct sysfs_directory *dir;
00311 
00312     is_probing = TRUE;
00313 
00314     /* traverse /sys/devices */
00315     strncpy (path, sysfs_mount_path, SYSFS_PATH_MAX);
00316     strncat (path, SYSFS_DEVICES_DIR, SYSFS_PATH_MAX);
00317 
00318     dir = sysfs_open_directory (path);
00319     if (dir == NULL) {
00320         DIE (("Error opening sysfs directory at %s\n", path));
00321     }
00322     if (sysfs_read_directory (dir) != 0) {
00323         DIE (("Error reading sysfs directory at %s\n", path));
00324     }
00325     if (dir->subdirs != NULL) {
00326         dlist_for_each_data (dir->subdirs, current,
00327                      struct sysfs_directory) {
00328             visit_device (current->path, TRUE);
00329         }
00330     }
00331     sysfs_close_directory (dir);
00332 
00333     /* visit class devices in /sys/class/video4linux */
00334     visit_class ("video4linux", TRUE);
00335 
00336     /* visit class devices in /sys/class/scsi_host */
00337     visit_class ("scsi_host", FALSE);
00338 
00339     /* visit class devices in /sys/class/scsi_host */
00340     visit_class ("scsi_device", FALSE);
00341 
00342     /* visit all block devices */
00343     visit_class ("block", TRUE);
00344 
00345     /* visit all net devices */
00346     visit_class ("net", TRUE);
00347 
00348     /* input devices are not yet in sysfs */
00349     linux_class_input_probe ();
00350 
00351     is_probing = FALSE;
00352 
00353     /* Notify various device and class types that detection is done, so 
00354      * they can do some (optional) batch processing
00355      */
00356     linux_pci_detection_done ();
00357     linux_usb_detection_done ();
00358     /*linux_ieee1394_detection_done(); */
00359     linux_ide_detection_done ();
00360     linux_class_block_detection_done ();
00361     linux_class_input_detection_done ();
00362     linux_class_net_detection_done ();
00363     linux_class_scsi_detection_done ();
00364 }
00365 
00367 #define HOTPLUG_INPUT_MAX 128
00368 
00377 static DBusHandlerResult
00378 handle_hotplug (DBusConnection * connection, DBusMessage * message)
00379 {
00380     DBusMessageIter iter;
00381     DBusMessageIter dict_iter;
00382     dbus_bool_t is_add;
00383     char *subsystem;
00384     char input_name[HOTPLUG_INPUT_MAX];
00385     char input_phys[HOTPLUG_INPUT_MAX];
00386     char input_key[HOTPLUG_INPUT_MAX];
00387     int input_ev = 0;
00388     int input_rel = 0;
00389     int input_abs = 0;
00390     int input_led = 0;
00391     char sysfs_devpath[SYSFS_PATH_MAX];
00392 
00393     sysfs_devpath[0] = '\0';
00394     input_name[0] = input_phys[0] = input_key[0] = '\0';
00395 
00396     dbus_message_iter_init (message, &iter);
00397 
00398     if (dbus_message_iter_get_arg_type (&iter) != DBUS_TYPE_STRING) {
00400         dbus_message_unref (message);
00401         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
00402     }
00403     subsystem = dbus_message_iter_get_string (&iter);
00404 
00405     dbus_message_iter_next (&iter);
00406     dbus_message_iter_init_dict_iterator (&iter, &dict_iter);
00407 
00408     is_add = FALSE;
00409 
00410     for (; dbus_message_iter_has_next (&dict_iter);
00411          dbus_message_iter_next (&dict_iter)) {
00412         char *key;
00413         char *value;
00414 
00415         key = dbus_message_iter_get_dict_key (&dict_iter);
00416         value = dbus_message_iter_get_string (&dict_iter);
00417 
00418         /*printf("  key/value = %s |-> %s\n", key, value); */
00419 
00420         if (strcmp (key, "ACTION") == 0) {
00421             if (strcmp (value, "add") == 0) {
00422                 is_add = TRUE;
00423             }
00424         } else if (strcmp (key, "DEVPATH") == 0) {
00425             strncpy (sysfs_devpath, sysfs_mount_path,
00426                  SYSFS_PATH_MAX);
00427             strncat (sysfs_devpath, value, SYSFS_PATH_MAX);
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         if (is_add) {
00455             HAL_INFO (("Adding device @ sysfspath %s",
00456                    sysfs_devpath));
00457             visit_device (sysfs_devpath, FALSE);
00458         } else {
00459             HalDevice *d;
00460 
00461             d = ds_device_find_by_key_value_string
00462                 ("linux.sysfs_path", sysfs_devpath, TRUE);
00463             if (d == NULL) {
00464                 HAL_WARNING (("Couldn't remove device @ %s "
00465                           "on hotplug remove", 
00466                           sysfs_devpath));
00467             } else {
00468                 HAL_INFO (("Removing device @ sysfspath %s, "
00469                        "udi %s", sysfs_devpath, d->udi));
00470 
00471                 if (ds_property_exists
00472                     (d, "info.persistent")
00473                     && ds_property_get_bool (
00474                         d, "info.persistent"))
00475                 {
00476                     ds_property_set_bool (
00477                         d, "info.not_available", TRUE);
00478                     /* Remove enough specific details so 
00479                      * we are not found by child devices 
00480                      * when being plugged in again.. 
00481                      */
00482                     ds_property_remove (d, "info.parent");
00483                     ds_property_remove (
00484                         d, "info.physical_device");
00485                     ds_property_remove (
00486                         d, "linux.sysfs_path");
00487                     ds_property_remove (
00488                         d, "linux.sysfs_path_device");
00489                     HAL_INFO (("Device %s is persistent, "
00490                            "so not removed", d->udi));
00491                 } else {
00492                     ds_device_destroy (d);
00493                 }
00494             }
00495         }
00496     } else if (sysfs_devpath[0] != '\0' &&
00497            (strcmp (subsystem, "net") == 0 ||
00498             strcmp (subsystem, "block") == 0 ||
00499             strcmp (subsystem, "scsi_host") == 0 ||
00500             strcmp (subsystem, "scsi_device") == 0 ||
00501             strcmp (subsystem, "i2c-adapter") == 0 ||
00502             strcmp (subsystem, "video4linux") == 0)) {
00503         if (is_add) {
00504             HAL_INFO (("Adding classdevice @ sysfspath %s",
00505                    sysfs_devpath));
00506             visit_class_device (sysfs_devpath, FALSE);
00507         } else {
00508             HalDevice *d;
00509             d = ds_device_find_by_key_value_string
00510                 ("linux.sysfs_path", sysfs_devpath, TRUE);
00511             if (d == NULL) {
00512                 HAL_WARNING (("Couldn't remove device @ %s on "
00513                           "hotplug remove",sysfs_devpath));
00514             } else {
00515                 if (strcmp (subsystem, "block") == 0)
00516                     linux_class_block_removed (d);
00517 
00518                 HAL_INFO (("Removing classdevice @ sysfspath "
00519                        "%s, udi %s",sysfs_devpath,d->udi));
00520                 ds_device_destroy (d);
00521             }
00522         }
00523     } else if (strcmp (subsystem, "input") == 0) {
00524         if (is_add) {
00528             linux_class_input_handle_hotplug_add (input_name,
00529                                   input_phys,
00530                                   input_key,
00531                                   input_ev,
00532                                   input_rel,
00533                                   input_abs,
00534                                   input_led);
00535         } else {
00536             /* This block is left intentionally blank, since input
00537              * class devices simply attach to other (physical)
00538              * devices and when they disappear, the input part 
00539              * also goes away
00540              */
00541         }
00542     }
00543 
00544     return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
00545 }
00546 
00547 /* fwd decl */
00548 static void handle_udev_node_created_found_device (HalDevice * d,
00549                            void *data1,
00550                            void *data2);
00551 
00560 static DBusHandlerResult
00561 handle_udev_node_created (DBusConnection * connection,
00562               DBusMessage * message)
00563 {
00564     char *filename;
00565     char *sysfs_path;
00566     char sysfs_dev_path[SYSFS_PATH_MAX];
00567 
00568     if (dbus_message_get_args (message, NULL,
00569                    DBUS_TYPE_STRING, &filename,
00570                    DBUS_TYPE_STRING, &sysfs_path,
00571                    DBUS_TYPE_INVALID)) {
00572         strncpy (sysfs_dev_path, sysfs_mount_path, SYSFS_PATH_MAX);
00573         strncat (sysfs_dev_path, sysfs_path, SYSFS_PATH_MAX);
00574         HAL_INFO (("udev NodeCreated: %s %s\n", filename,
00575                sysfs_dev_path));
00576 
00577         /* Find block device; this happens asynchronously as our it 
00578          * might be added later..
00579          */
00580         ds_device_async_find_by_key_value_string (
00581             "linux.sysfs_path_device", sysfs_dev_path, FALSE,
00582             /* note: it doesn't need to be in the GDL */
00583             handle_udev_node_created_found_device,
00584             (void *) filename, NULL,
00585             HAL_LINUX_HOTPLUG_TIMEOUT);
00586 
00587         /* NOTE NOTE NOTE: we will free filename in async 
00588          * result function 
00589          */
00590 
00591         dbus_free (sysfs_path);
00592     }
00593 
00594     return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
00595 }
00596 
00603 static void
00604 handle_udev_node_created_found_device (HalDevice * d,
00605                        void *data1, void *data2)
00606 {
00607     char *filename = (char *) data1;
00608 
00609     if (d != NULL) {
00610         HAL_INFO (("Setting block.device=%s for udi=%s", filename,
00611                d->udi));
00612         ds_property_set_string (d, "block.device", filename);
00613         linux_class_block_check_if_ready_to_add (d);
00614     } else {
00615         HAL_WARNING (("No HAL device corresponding to device %s",
00616                   filename));
00617     }
00618 
00619     dbus_free (filename);
00620 }
00621 
00630 DBusHandlerResult
00631 osspec_filter_function (DBusConnection * connection,
00632             DBusMessage * message, void *user_data)
00633 {
00634 
00635 /*
00636     HAL_INFO(("obj_path=%s interface=%s method=%s", 
00637               dbus_message_get_path(message), 
00638               dbus_message_get_interface(message),
00639               dbus_message_get_member(message)));
00640 */
00641 
00642     if (dbus_message_is_method_call (message,
00643                      "org.freedesktop.Hal.Linux.Hotplug",
00644                      "HotplugEvent") &&
00645         strcmp (dbus_message_get_path (message),
00646             "/org/freedesktop/Hal/Linux/Hotplug") == 0) {
00647         return handle_hotplug (connection, message);
00648     } else if (dbus_message_is_signal (message,
00649                        "org.kernel.udev.NodeMonitor",
00650                        "NodeCreated")) {
00651         return handle_udev_node_created (connection, message);
00652     } else
00653         if (dbus_message_is_signal
00654         (message, "org.kernel.udev.NodeMonitor", "NodeDeleted")) {
00655 
00656         /* This is left intentionally blank since this it means that a
00657          * block device is removed and we'll catch that other places
00658          */
00659 
00660     }
00661 
00662     return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
00663 }
00664 

Generated on Thu Mar 11 21:32:22 2004 for HAL by doxygen 1.3.6-20040222