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

osspec.c

00001 /***************************************************************************
00002  * CVSID: $Id: osspec.c,v 1.14 2004/04/22 21:52:05 david Exp $
00003  *
00004  * Detection and monitoring of devices on Linux 2.6 + udev
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 #include <dbus/dbus-glib.h>
00041 
00042 #include "../osspec.h"
00043 #include "../logger.h"
00044 #include "../hald.h"
00045 
00046 #include "common.h"
00047 #include "bus_device.h"
00048 #include "class_device.h"
00049 
00050 #include "libsysfs/libsysfs.h"
00051 
00052 extern ClassDeviceHandler input_class_handler;
00053 extern ClassDeviceHandler net_class_handler;
00054 extern ClassDeviceHandler printer_class_handler;
00055 extern ClassDeviceHandler scsi_host_class_handler;
00056 extern ClassDeviceHandler scsi_device_class_handler;
00057 extern ClassDeviceHandler scsi_generic_class_handler;
00058 extern ClassDeviceHandler block_class_handler;
00059 extern ClassDeviceHandler pcmcia_socket_class_handler;
00060 
00061 /*
00062 extern ClassDeviceHandler ieee1394_host_class_handler;
00063 extern ClassDeviceHandler ieee1394_node_class_handler;
00064 */
00065 
00066 extern BusDeviceHandler pci_bus_handler;
00067 extern BusDeviceHandler usb_bus_handler;
00068 extern BusDeviceHandler usbif_bus_handler;
00069 extern BusDeviceHandler ide_host_bus_handler;
00070 extern BusDeviceHandler ide_bus_handler;
00071 
00072 /*
00073  * NOTE!  Order can be significant here, especially at startup time
00074  * when we're probing.  If we're expecting to find a parent device
00075  * and it's not there, things will complain.  (scsi_generic needs to
00076  * be after scsi_host, for example.)
00077  */
00078 static ClassDeviceHandler* class_device_handlers[] = {
00079     &input_class_handler,
00080     &net_class_handler,
00081     &printer_class_handler,
00082     &scsi_host_class_handler,
00083     &scsi_device_class_handler,
00084     &scsi_generic_class_handler,
00085     &block_class_handler,
00086     &pcmcia_socket_class_handler,
00087     /*&ieee1394_host_class_handler,
00088       &ieee1394_node_class_handler,*/
00089     NULL
00090 };
00091 
00092 static BusDeviceHandler* bus_device_handlers[] = {
00093     &pci_bus_handler,
00094     &usb_bus_handler,
00095     &usbif_bus_handler,
00096     &ide_host_bus_handler,
00097         &ide_bus_handler,
00098     NULL
00099 };
00100 
00109 char sysfs_mount_path[SYSFS_PATH_MAX];
00110 
00125 static void
00126 visit_class_device (const char *path, dbus_bool_t visit_children)
00127 {
00128     int i;
00129     struct sysfs_class_device *class_device;
00130     struct sysfs_directory *subdir;
00131 
00132     class_device = sysfs_open_class_device (path);
00133     if (class_device == NULL) {
00134         HAL_WARNING (("Coulnd't get sysfs class device object at "
00135                   "path %s", path));
00136         return;
00137     }
00138 
00139     HAL_INFO (("*** classname=%s path=%s",
00140            class_device->classname,
00141            class_device->path));
00142 
00143     for (i=0; class_device_handlers[i] != NULL; i++) {
00144         ClassDeviceHandler *ch = class_device_handlers[i];
00145         if (ch->accept (ch, path, class_device, is_probing))
00146             ch->visit (ch, path, class_device, is_probing);
00147     }
00148 
00149     /* Visit children */
00150     if (visit_children && class_device->directory != NULL &&
00151         class_device->directory->subdirs != NULL) {
00152         dlist_for_each_data (class_device->directory->subdirs,
00153                      subdir, struct sysfs_directory) {
00154             char newpath[SYSFS_PATH_MAX];
00155             snprintf (newpath, SYSFS_PATH_MAX, "%s/%s", path,
00156                   subdir->name);
00157             visit_class_device (newpath, TRUE);
00158         }
00159     }
00160 
00161     sysfs_close_class_device (class_device);
00162 }
00163 
00174 static void
00175 visit_class (const char *class_name, dbus_bool_t visit_children)
00176 {
00177     struct sysfs_class *cls = NULL;
00178     struct sysfs_class_device *cur = NULL;
00179 
00180     cls = sysfs_open_class (class_name);
00181     if (cls == NULL) {
00182         HAL_ERROR (("Error opening class %s\n", class_name));
00183         return;
00184     }
00185 
00186     if (cls->devices != NULL) {
00187         dlist_for_each_data (cls->devices, cur,
00188                      struct sysfs_class_device) {
00189             visit_class_device (cur->path, visit_children);
00190         }
00191     }
00192 
00193     sysfs_close_class (cls);
00194 }
00195 
00209 static void
00210 visit_device (const char *path, dbus_bool_t visit_children)
00211 {
00212     int i;
00213     struct sysfs_device *device;
00214     struct sysfs_directory *subdir;
00215 
00216     device = sysfs_open_device (path);
00217     if (device == NULL) {
00218         HAL_WARNING (("Coulnd't get sysfs device object at path %s",
00219                   path));
00220         return;
00221     }
00222 
00223     HAL_INFO (("$$$ busname=%s path=%s", device->bus, device->path));
00224 
00225     /*HAL_INFO ((" path=%s", path));*/
00226 
00227     for (i=0; bus_device_handlers[i] != NULL; i++) {
00228         BusDeviceHandler *bh = bus_device_handlers[i];
00229         if (bh->accept (bh, path, device, is_probing))
00230             bh->visit (bh, path, device, is_probing);
00231     }
00232 
00233     /* Visit children */
00234     if (visit_children && device->directory->subdirs != NULL) {
00235         dlist_for_each_data (device->directory->subdirs, subdir,
00236                      struct sysfs_directory) {
00237             char newpath[SYSFS_PATH_MAX];
00238             snprintf (newpath, SYSFS_PATH_MAX, "%s/%s", path,
00239                   subdir->name);
00240             visit_device (newpath, TRUE);
00241         }
00242     }
00243 
00244     sysfs_close_device (device);
00245 }
00246 
00247 
00253 static gboolean
00254 osspec_timer_handler (gpointer data)
00255 {
00256     int i;
00257 
00258     for (i=0; bus_device_handlers[i] != NULL; i++) {
00259         BusDeviceHandler *bh = bus_device_handlers[i];
00260         bh->tick (bh);
00261     }
00262 
00263     for (i=0; class_device_handlers[i] != NULL; i++) {
00264         ClassDeviceHandler *ch = class_device_handlers[i];
00265         ch->tick (ch);
00266     }
00267 
00268     return TRUE;
00269 }
00270 
00271 /* This function is documented in ../osspec.h */
00272 void
00273 osspec_init (DBusConnection * dbus_connection)
00274 {
00275     int i;
00276     int rc;
00277     DBusError error;
00278 
00279     /* get mount path for sysfs */
00280     rc = sysfs_get_mnt_path (sysfs_mount_path, SYSFS_PATH_MAX);
00281     if (rc != 0) {
00282         DIE (("Couldn't get mount path for sysfs"));
00283     }
00284     HAL_INFO (("Mountpoint for sysfs is %s", sysfs_mount_path));
00285 
00286     for (i=0; bus_device_handlers[i] != NULL; i++) {
00287         BusDeviceHandler *bh = bus_device_handlers[i];
00288         bh->init (bh);
00289     }
00290 
00291     for (i=0; class_device_handlers[i] != NULL; i++) {
00292         ClassDeviceHandler *ch = class_device_handlers[i];
00293         ch->init (ch);
00294     }
00295 
00296     /* Add match for signals from udev */
00297     dbus_error_init (&error);
00298     dbus_bus_add_match (dbus_connection,
00299                 "type='signal',"
00300                 "interface='org.kernel.udev.NodeMonitor',"
00301                 /*"sender='org.kernel.udev'," until dbus is fixed*/
00302                 "path='/org/kernel/udev/NodeMonitor'", &error);
00303     if (dbus_error_is_set (&error)) {
00304         HAL_WARNING (("Cannot subscribe to udev signals, error=%s",
00305                   error.message));
00306     }
00307 
00308     /* Setup timer */
00309     g_timeout_add (2000, osspec_timer_handler, NULL);   
00310 }
00311 
00313 dbus_bool_t is_probing;
00314 
00315 /* This function is documented in ../osspec.h */
00316 void
00317 osspec_probe ()
00318 {
00319     int i;
00320     char path[SYSFS_PATH_MAX];
00321     struct sysfs_directory *current;
00322     struct sysfs_directory *dir;
00323 
00324     is_probing = TRUE;
00325 
00326     /* traverse /sys/devices */
00327     strncpy (path, sysfs_mount_path, SYSFS_PATH_MAX);
00328     strncat (path, SYSFS_DEVICES_DIR, SYSFS_PATH_MAX);
00329 
00330     dir = sysfs_open_directory (path);
00331     if (dir == NULL) {
00332         DIE (("Error opening sysfs directory at %s\n", path));
00333     }
00334     if (sysfs_read_directory (dir) != 0) {
00335         DIE (("Error reading sysfs directory at %s\n", path));
00336     }
00337     if (dir->subdirs != NULL) {
00338         dlist_for_each_data (dir->subdirs, current,
00339                      struct sysfs_directory) {
00340             visit_device (current->path, TRUE);
00341         }
00342     }
00343     sysfs_close_directory (dir);
00344 
00345     for (i=0; class_device_handlers[i] != NULL; i++) {
00346         ClassDeviceHandler *ch = class_device_handlers[i];
00347         visit_class (ch->sysfs_class_name, TRUE);
00349     }
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     for (i=0; bus_device_handlers[i] != NULL; i++) {
00357         BusDeviceHandler *bh = bus_device_handlers[i];
00358         bh->detection_done (bh);
00359     }
00360 
00361     for (i=0; class_device_handlers[i] != NULL; i++) {
00362         ClassDeviceHandler *ch = class_device_handlers[i];
00363         ch->detection_done (ch);
00364     }
00365 }
00366 
00367 static void
00368 remove_device (const char *path, const char *subsystem)
00369 
00370 {   HalDevice *d;
00371 
00372     d = hal_device_store_match_key_value_string (hald_get_gdl (), 
00373                              "linux.sysfs_path",
00374                              path);
00375 
00376     if (d == NULL) {
00377         HAL_WARNING (("Couldn't remove device @ %s on hotplug remove", 
00378                   path));
00379     } else {
00380         /*HAL_INFO (("Removing device @ sysfspath %s, udi %s", 
00381           path, d->udi));*/
00382         
00383         hal_device_store_remove (hald_get_gdl (), d);
00384     }
00385 }
00386 
00387 static void
00388 remove_class_device (const char *path, const char *subsystem)
00389 {
00390     int i;
00391     const char *bus_name;
00392     HalDevice *d;
00393 
00394     d = hal_device_store_match_key_value_string (hald_get_gdl (), 
00395                              "linux.sysfs_path",
00396                              path);
00397 
00398     if (d == NULL) {
00399         /* Right now we only handle class devices that are put in the
00400          * tree rather than merged, ie. merge_or_add is FALSE. That
00401          * happens in the other branch below.
00402          *
00403          * What we need to do here is to unmerge the device from the
00404          * sysdevice it belongs to. Ughh.. It's only a big deal when
00405          * loading/unloading drivers and this should never happen
00406          * on a desktop anyway?
00407          *
00408          * @todo FIXME
00409          */
00410 
00411         HAL_WARNING (("Removal of class device @ %s on "
00412                   "hotplug remove is not yet implemented", path));
00413 
00414     } else {
00415         /*HAL_INFO (("Removing device @ sysfspath %s, udi %s", 
00416           path, d->udi));*/
00417 
00418         bus_name = hal_device_property_get_string (d, "info.bus");
00419 
00420         for (i=0; class_device_handlers[i] != NULL; i++) {
00421             ClassDeviceHandler *ch = class_device_handlers[i];
00422             
00423             /* See class_device_visit() where this is merged */
00424             if (strcmp (ch->hal_class_name, bus_name) == 0) {
00425                 ch->removed (ch, path, d);
00426             }
00427         }
00428         
00429         hal_device_store_remove (hald_get_gdl (), d);
00430     }
00431 
00432     
00433 
00434     /* For now, just call the normal remove_device */
00435     remove_device (path, subsystem);
00436 }
00437 
00446 static DBusHandlerResult
00447 handle_hotplug (DBusConnection * connection, DBusMessage * message)
00448 {
00449     DBusMessageIter iter;
00450     DBusMessageIter dict_iter;
00451     dbus_bool_t is_add;
00452     char *subsystem;
00453     char sysfs_devpath[SYSFS_PATH_MAX];
00454     char sysfs_devpath_wo_mp[SYSFS_PATH_MAX];
00455 
00456     sysfs_devpath[0] = '\0';
00457 
00458     dbus_message_iter_init (message, &iter);
00459 
00460     if (dbus_message_iter_get_arg_type (&iter) != DBUS_TYPE_STRING) {
00462         dbus_message_unref (message);
00463         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
00464     }
00465     subsystem = dbus_message_iter_get_string (&iter);
00466 
00467     dbus_message_iter_next (&iter);
00468     dbus_message_iter_init_dict_iterator (&iter, &dict_iter);
00469 
00470     is_add = FALSE;
00471 
00472     do {
00473         char *key;
00474         char *value;
00475 
00476         key = dbus_message_iter_get_dict_key (&dict_iter);
00477         value = dbus_message_iter_get_string (&dict_iter);
00478 
00479         /*HAL_INFO (("key/value : %s=%s", key, value));*/
00480 
00481         if (strcmp (key, "ACTION") == 0) {
00482             if (strcmp (value, "add") == 0) {
00483                 is_add = TRUE;
00484             }
00485         } else if (strcmp (key, "DEVPATH") == 0) {
00486             strncpy (sysfs_devpath, sysfs_mount_path,
00487                  SYSFS_PATH_MAX);
00488             strncat (sysfs_devpath, value, SYSFS_PATH_MAX);
00489             strncpy (sysfs_devpath_wo_mp, value, SYSFS_PATH_MAX);
00490         }
00491     } while (dbus_message_iter_has_next (&dict_iter) &&
00492          dbus_message_iter_next (&dict_iter));
00493 
00494     /* ignore events without DEVPATH */
00495     if (sysfs_devpath[0] == '\0')
00496         goto out;
00497 
00498     HAL_INFO (("HotplugEvent %s, subsystem=%s devpath=%s foo=%s",
00499            (is_add ? "add" : "remove"), subsystem,
00500            sysfs_devpath[0] != '\0' ? sysfs_devpath : "(none)",
00501            sysfs_devpath_wo_mp));
00502 
00503     /* See if this is a class device or a bus device */
00504     if (strncmp (sysfs_devpath_wo_mp, "/block", 6)==0 ||
00505         strncmp (sysfs_devpath_wo_mp, "/class", 6)==0 ) {
00506         /* handle class devices */
00507         if (is_add)
00508             visit_class_device (sysfs_devpath, FALSE);
00509         else
00510             remove_class_device (sysfs_devpath, subsystem);
00511     } else {
00512         /* handle bus devices */
00513         if (is_add)
00514             visit_device (sysfs_devpath, FALSE);
00515         else
00516             remove_device (sysfs_devpath, subsystem);
00517     }
00518         
00519 
00520 out:
00521     return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
00522 }
00523 
00524 /* fwd decl */
00525 static void handle_udev_node_created_found_device (HalDevice * d,
00526                            void *data1,
00527                            void *data2);
00528 
00529 static void
00530 udev_node_created_cb (HalDeviceStore *store, HalDevice *device,
00531               gpointer user_data)
00532 {
00533     const char *filename = user_data;
00534 
00535     handle_udev_node_created_found_device (device, (void*) filename, NULL);
00536 }
00537 
00538 
00547 static DBusHandlerResult
00548 handle_device_event (DBusConnection * connection,
00549              DBusMessage * message)
00550 {
00551     dbus_bool_t is_add;
00552     char *filename;
00553     char *sysfs_path;
00554     char sysfs_dev_path[SYSFS_PATH_MAX];
00555 
00556     if (dbus_message_get_args (message, NULL,
00557                    DBUS_TYPE_BOOLEAN, &is_add,
00558                    DBUS_TYPE_STRING, &filename,
00559                    DBUS_TYPE_STRING, &sysfs_path,
00560                    DBUS_TYPE_INVALID)) {
00561         strncpy (sysfs_dev_path, sysfs_mount_path, SYSFS_PATH_MAX);
00562         strncat (sysfs_dev_path, sysfs_path, SYSFS_PATH_MAX);
00563 
00564         if (is_add ) {
00565 
00566             HAL_INFO (("DeviceEvent add devpath=%s devfile=%s",
00567                    sysfs_dev_path, filename));
00568 
00569             hal_device_store_match_key_value_string_async (
00570                 hald_get_tdl (),
00571                 ".udev.sysfs_path",
00572                 sysfs_dev_path,
00573                 udev_node_created_cb, filename,
00574                 HAL_LINUX_HOTPLUG_TIMEOUT);
00575 
00576             /* NOTE NOTE NOTE: we will free filename in async 
00577              * result function 
00578              */
00579         } else {
00580             dbus_free (filename);
00581         }
00582 
00583         dbus_free (sysfs_path);
00584     }
00585 
00586     return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
00587 }
00588 
00595 static void
00596 handle_udev_node_created_found_device (HalDevice * d,
00597                        void *data1, void *data2)
00598 {
00599     int i;
00600     const char *sysfs_class_name;
00601     char *dev_file = (char *) data1;
00602 
00603     if (d != NULL) {
00604         HAL_INFO (("dev_file=%s is for udi=%s", dev_file, d->udi));
00605 
00606         sysfs_class_name = 
00607             hal_device_property_get_string (d, ".udev.class_name");
00608 
00609         /*HAL_INFO ((".udev.class_name = %s", sysfs_class_name));*/
00610 
00611         for (i=0; class_device_handlers[i] != NULL; i++) {
00612             ClassDeviceHandler *ch = class_device_handlers[i];
00613             if (strcmp (ch->sysfs_class_name, sysfs_class_name) == 0) {
00614                 ch->udev_event (ch, d, dev_file);
00615             }
00616         }
00617     } else {
00618         HAL_WARNING (("No HAL device corresponding to device file %s",
00619                   dev_file));
00620     }
00621 
00622     dbus_free (dev_file);
00623 }
00624 
00633 DBusHandlerResult
00634 osspec_filter_function (DBusConnection * connection,
00635             DBusMessage * message, void *user_data)
00636 {
00637 
00638     if (dbus_message_is_method_call (message,
00639                      "org.freedesktop.Hal.Linux.Hotplug",
00640                      "HotplugEvent") &&
00641         strcmp (dbus_message_get_path (message),
00642             "/org/freedesktop/Hal/Linux/Hotplug") == 0) {
00643         return handle_hotplug (connection, message);
00644     } else if (dbus_message_is_method_call (message,
00645                         "org.freedesktop.Hal.Linux.Hotplug",
00646                         "DeviceEvent") &&
00647         strcmp (dbus_message_get_path (message),
00648             "/org/freedesktop/Hal/Linux/Hotplug") == 0) {
00649         return handle_device_event (connection, message);
00650     } 
00651 
00652     return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
00653 }
00654 

Generated on Sat Apr 24 19:57:45 2004 for HAL by doxygen 1.3.6-20040222