00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
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
00098
00099
00100
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
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
00190
00191
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
00201
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
00212
00213
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
00242 }
00243
00244
00245
00246
00247 }
00248
00249
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
00266 void
00267 osspec_init (DBusConnection * dbus_connection)
00268 {
00269 int rc;
00270 DBusError error;
00271
00272
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
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
00289 dbus_error_init (&error);
00290 dbus_bus_add_match (dbus_connection,
00291 "type='signal',"
00292 "interface='org.kernel.udev.NodeMonitor',"
00293
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
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
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
00334 visit_class ("video4linux", TRUE);
00335
00336
00337 visit_class ("scsi_host", FALSE);
00338
00339
00340 visit_class ("scsi_device", FALSE);
00341
00342
00343 visit_class ("block", TRUE);
00344
00345
00346 visit_class ("net", TRUE);
00347
00348
00349 linux_class_input_probe ();
00350
00351 is_probing = FALSE;
00352
00353
00354
00355
00356 linux_pci_detection_done ();
00357 linux_usb_detection_done ();
00358
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
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
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
00479
00480
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
00537
00538
00539
00540
00541 }
00542 }
00543
00544 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
00545 }
00546
00547
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
00578
00579
00580 ds_device_async_find_by_key_value_string (
00581 "linux.sysfs_path_device", sysfs_dev_path, FALSE,
00582
00583 handle_udev_node_created_found_device,
00584 (void *) filename, NULL,
00585 HAL_LINUX_HOTPLUG_TIMEOUT);
00586
00587
00588
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
00637
00638
00639
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
00657
00658
00659
00660 }
00661
00662 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
00663 }
00664