/*************************************************************************** * * devinfo.c : main file for libdevinfo-based device enumeration * * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * * Licensed under the Academic Free License version 2.1 * **************************************************************************/ #pragma ident "%Z%%M% %I% %E% SMI" #include #include #include #include "../osspec.h" #include "../logger.h" #include "../hald.h" #include "../hald_dbus.h" #include "../device_info.h" #include "../util.h" #include "../hald_runner.h" #include "osspec_solaris.h" #include "hotplug.h" #include "devinfo.h" #include "devinfo_pci.h" #include "devinfo_storage.h" #include "devinfo_ieee1394.h" #include "devinfo_usb.h" #include "devinfo_misc.h" void devinfo_add_subtree(HalDevice *parent, di_node_t node, gboolean is_root); HalDevice *devinfo_add_node(HalDevice *parent, di_node_t node); void devinfo_add(HalDevice *parent, gchar *path) { di_node_t root; if (strcmp (path, "/") == 0) { if ((root = di_init(path, DINFOCACHE)) == DI_NODE_NIL) { HAL_INFO (("di_init() failed %d", errno)); return; } } else { if ((root = di_init(path, DINFOCPYALL)) == DI_NODE_NIL) { HAL_INFO (("di_init() failed %d", errno)); return; } } devinfo_add_subtree(parent, root, TRUE); di_fini (root); } void devinfo_add_subtree(HalDevice *parent, di_node_t node, gboolean is_root) { HalDevice *d; di_node_t root_node, child_node; HAL_INFO (("add_subtree: %s", di_node_name (node))); root_node = node; do { d = devinfo_add_node (parent, node); if ((d != NULL) && (child_node = di_child_node (node)) != DI_NODE_NIL) { devinfo_add_subtree (d, child_node, FALSE); } node = di_sibling_node (node); } while ((node != DI_NODE_NIL) && (!is_root || di_parent_node (node) == root_node)); } void devinfo_set_default_properties (HalDevice *d, HalDevice *parent, di_node_t node, char *devfs_path) { char *driver_name, *s; const char *s1; char udi[HAL_PATH_MAX]; if (parent != NULL) { hal_device_property_set_string (d, "info.parent", parent->udi); } else { hal_device_property_set_string (d, "info.parent", "/org/freedesktop/Hal/devices/local"); } hal_util_compute_udi (hald_get_gdl (), udi, sizeof (udi), "/org/freedesktop/Hal/devices%s_%d", devfs_path, di_instance (node)); hal_device_set_udi (d, udi); hal_device_property_set_string (d, "info.udi", udi); if (di_prop_lookup_strings (DDI_DEV_T_ANY, node, "model", &s) > 0) { hal_device_property_set_string (d, "info.product", s); } else { hal_device_property_set_string (d, "info.product", di_node_name (node)); } hal_device_property_set_string (d, "solaris.devfs_path", devfs_path); if ((driver_name = di_driver_name (node)) != NULL) { hal_device_property_set_string (d, "info.solaris.driver", driver_name); } /* inherit parent's claim attributes */ if (hal_device_property_get_bool (parent, "info.claimed")) { s1 = hal_device_property_get_string (parent, "info.claimed.service"); if (s1 != NULL) { hal_device_property_set_bool (d, "info.claimed", TRUE); hal_device_property_set_string (d, "info.claimed.service", s1); } } } /* device handlers, ordered specific to generic */ static DevinfoDevHandler *devinfo_handlers[] = { &devinfo_computer_handler, &devinfo_cpu_handler, &devinfo_ide_handler, &devinfo_scsi_handler, &devinfo_floppy_handler, &devinfo_usb_handler, &devinfo_ieee1394_handler, &devinfo_pci_handler, &devinfo_lofi_handler, &devinfo_default_handler, NULL }; HalDevice * devinfo_add_node(HalDevice *parent, di_node_t node) { HalDevice *d = NULL; char *devfs_path; char *device_type = NULL; DevinfoDevHandler *handler; int i; devfs_path = di_devfs_path (node); (void) di_prop_lookup_strings (DDI_DEV_T_ANY, node, "device_type", &device_type); for (i = 0; (d == NULL) && (devinfo_handlers[i] != NULL); i++) { handler = devinfo_handlers[i]; d = handler->add (parent, node, devfs_path, device_type); } di_devfs_path_free(devfs_path); HAL_INFO (("add_node: %s", d ? d->udi : "none")); return (d); } void devinfo_hotplug_enqueue(HalDevice *d, gchar *devfs_path, DevinfoDevHandler *handler, int action, int front) { HotplugEvent *hotplug_event; hotplug_event = g_new0 (HotplugEvent, 1); hotplug_event->action = action; hotplug_event->type = HOTPLUG_EVENT_DEVFS; hotplug_event->d = d; strlcpy (hotplug_event->un.devfs.devfs_path, devfs_path, sizeof (hotplug_event->un.devfs.devfs_path)); hotplug_event->un.devfs.handler = handler; hotplug_event_enqueue (hotplug_event, front); } void devinfo_add_enqueue(HalDevice *d, gchar *devfs_path, DevinfoDevHandler *handler) { devinfo_hotplug_enqueue (d, devfs_path, handler, HOTPLUG_ACTION_ADD, 0); } void devinfo_add_enqueue_at_front(HalDevice *d, gchar *devfs_path, DevinfoDevHandler *handler) { devinfo_hotplug_enqueue (d, devfs_path, handler, HOTPLUG_ACTION_ADD, 1); } void devinfo_remove_enqueue(gchar *devfs_path, DevinfoDevHandler *handler) { devinfo_hotplug_enqueue (NULL, devfs_path, handler, HOTPLUG_ACTION_REMOVE, 0); } void devinfo_callouts_add_done (HalDevice *d, gpointer userdata1, gpointer userdata2) { void *end_token = (void *) userdata1; /* Move from temporary to global device store */ hal_device_store_remove (hald_get_tdl (), d); hal_device_store_add (hald_get_gdl (), d); hotplug_event_end (end_token); } void devinfo_callouts_probing_done (HalDevice *d, guint32 exit_type, gint return_code, char **error, gpointer userdata1, gpointer userdata2) { void *end_token = (void *) userdata1; /* Discard device if probing reports failure */ if (exit_type != HALD_RUN_SUCCESS || (return_code != 0)) { HAL_INFO (("Probing for %s failed %d", d->udi, return_code)); hal_device_store_remove (hald_get_tdl (), d); g_object_unref (d); hotplug_event_end (end_token); return; } /* Merge properties from .fdi files */ di_search_and_merge (d, DEVICE_INFO_TYPE_INFORMATION); di_search_and_merge (d, DEVICE_INFO_TYPE_POLICY); hal_util_callout_device_add (d, devinfo_callouts_add_done, end_token, NULL); } void devinfo_callouts_preprobing_done (HalDevice *d, gpointer userdata1, gpointer userdata2) { void *end_token = (void *) userdata1; DevinfoDevHandler *handler = (DevinfoDevHandler *) userdata2; void (*probing_done) (HalDevice *, guint32, gint, char **, gpointer, gpointer); const gchar *prober; int prober_timeout; if (hal_device_property_get_bool (d, "info.ignore")) { HAL_INFO (("Preprobing merged info.ignore==TRUE")); /* Leave device with info.ignore==TRUE so we won't pick up children */ hal_device_property_remove (d, "info.category"); hal_device_property_remove (d, "info.capabilities"); hal_device_store_remove (hald_get_tdl (), d); hal_device_store_add (hald_get_gdl (), d); hotplug_event_end (end_token); return; } if (handler != NULL && handler->get_prober != NULL) { prober = handler->get_prober (d, &prober_timeout); } else { prober = NULL; } if (handler->probing_done != NULL) { probing_done = handler->probing_done; } else { probing_done = devinfo_callouts_probing_done; } if (prober != NULL) { /* probe the device */ HAL_INFO(("Probing udi=%s", d->udi)); hald_runner_run (d, prober, NULL, prober_timeout, probing_done, (gpointer) end_token, (gpointer) handler); } else { probing_done (d, 0, 0, NULL, userdata1, userdata2); } } /* This is the beginning of hotplug even handling */ void hotplug_event_begin_add_devinfo (HalDevice *d, HalDevice *parent, DevinfoDevHandler *handler, void *end_token) { HAL_INFO(("Preprobing udi=%s", d->udi)); if (parent != NULL && hal_device_property_get_bool (parent, "info.ignore")) { HAL_INFO (("Ignoring device since parent has info.ignore==TRUE")); hotplug_event_end (end_token); return; } /* add to TDL so preprobing callouts and prober can access it */ hal_device_store_add (hald_get_tdl (), d); /* Process preprobe fdi files */ di_search_and_merge (d, DEVICE_INFO_TYPE_PREPROBE); /* Run preprobe callouts */ hal_util_callout_device_preprobe (d, devinfo_callouts_preprobing_done, end_token, handler); } void devinfo_remove (gchar *devfs_path) { devinfo_remove_enqueue ((gchar *)devfs_path, NULL); } /* generate hotplug event for each device in this branch */ void devinfo_remove_branch (gchar *devfs_path, HalDevice *d) { GSList *i; GSList *children; HalDevice *child; char *child_devfs_path; if (d == NULL) { d = hal_device_store_match_key_value_string (hald_get_gdl (), "solaris.devfs_path", devfs_path); if (d == NULL) return; } HAL_INFO (("remove_branch: %s %s\n", devfs_path, d->udi)); /* first remove children */ children = hal_device_store_match_multiple_key_value_string (hald_get_gdl(), "info.parent", d->udi); for (i = children; i != NULL; i = g_slist_next (i)) { child = HAL_DEVICE (i->data); HAL_INFO (("remove_branch: child %s\n", child->udi)); devinfo_remove_branch ((gchar *)hal_device_property_get_string (child, "solaris.devfs_path"), child); } g_slist_free (children); HAL_INFO (("remove_branch: done with children")); /* then remove self */ HAL_INFO (("remove_branch: queueing %s", devfs_path)); devinfo_remove_enqueue (devfs_path, NULL); } void devinfo_callouts_remove_done (HalDevice *d, gpointer userdata1, gpointer userdata2) { void *end_token = (void *) userdata1; HAL_INFO (("Remove callouts completed udi=%s", d->udi)); if (!hal_device_store_remove (hald_get_gdl (), d)) { HAL_WARNING (("Error removing device")); } g_object_unref (d); hotplug_event_end (end_token); } void hotplug_event_begin_remove_devinfo (HalDevice *d, gchar *devfs_path, void *end_token) { if (hal_device_has_capability (d, "volume")) { devinfo_volume_hotplug_begin_remove (d, devfs_path, end_token); } else { hal_util_callout_device_remove (d, devinfo_callouts_remove_done, end_token, NULL); } } gboolean devinfo_device_rescan (HalDevice *d) { if (hal_device_has_capability (d, "block")) { return (devinfo_storage_device_rescan (d)); } else { return (FALSE); } }