12912Sartem /***************************************************************************
22912Sartem *
32912Sartem * devinfo.c : main file for libdevinfo-based device enumeration
42912Sartem *
5*12426Sgdamore@opensolaris.org * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
62912Sartem *
72912Sartem * Licensed under the Academic Free License version 2.1
82912Sartem *
92912Sartem **************************************************************************/
102912Sartem
113121Sartem #ifdef HAVE_CONFIG_H
123121Sartem # include <config.h>
133121Sartem #endif
143121Sartem
152912Sartem #include <stdio.h>
162912Sartem #include <string.h>
172912Sartem #include <libdevinfo.h>
182912Sartem
192912Sartem #include "../osspec.h"
202912Sartem #include "../logger.h"
212912Sartem #include "../hald.h"
222912Sartem #include "../hald_dbus.h"
232912Sartem #include "../device_info.h"
242912Sartem #include "../util.h"
252912Sartem #include "../hald_runner.h"
262912Sartem #include "osspec_solaris.h"
272912Sartem #include "hotplug.h"
282912Sartem #include "devinfo.h"
292912Sartem #include "devinfo_pci.h"
302912Sartem #include "devinfo_storage.h"
312912Sartem #include "devinfo_ieee1394.h"
322912Sartem #include "devinfo_usb.h"
332912Sartem #include "devinfo_misc.h"
344035Sphitran #include "devinfo_acpi.h"
356654Snp146283 #include "devinfo_cpu.h"
362912Sartem
372912Sartem void devinfo_add_subtree(HalDevice *parent, di_node_t node, gboolean is_root);
382912Sartem HalDevice *devinfo_add_node(HalDevice *parent, di_node_t node);
392912Sartem
402912Sartem void
devinfo_add(HalDevice * parent,gchar * path)412912Sartem devinfo_add(HalDevice *parent, gchar *path)
422912Sartem {
432912Sartem di_node_t root;
442912Sartem
452912Sartem if (strcmp (path, "/") == 0) {
462912Sartem if ((root = di_init(path, DINFOCACHE)) == DI_NODE_NIL) {
472912Sartem HAL_INFO (("di_init() failed %d", errno));
482912Sartem return;
492912Sartem }
502912Sartem } else {
512912Sartem if ((root = di_init(path, DINFOCPYALL)) == DI_NODE_NIL) {
522912Sartem HAL_INFO (("di_init() failed %d", errno));
532912Sartem return;
542912Sartem }
552912Sartem }
562912Sartem
572912Sartem devinfo_add_subtree(parent, root, TRUE);
582912Sartem
592912Sartem di_fini (root);
602912Sartem }
612912Sartem
622912Sartem void
devinfo_add_subtree(HalDevice * parent,di_node_t node,gboolean is_root)632912Sartem devinfo_add_subtree(HalDevice *parent, di_node_t node, gboolean is_root)
642912Sartem {
652912Sartem HalDevice *d;
662912Sartem di_node_t root_node, child_node;
672912Sartem
682912Sartem HAL_INFO (("add_subtree: %s", di_node_name (node)));
692912Sartem
702912Sartem root_node = node;
712912Sartem do {
722912Sartem d = devinfo_add_node (parent, node);
732912Sartem
742912Sartem if ((d != NULL) &&
752912Sartem (child_node = di_child_node (node)) != DI_NODE_NIL) {
762912Sartem devinfo_add_subtree (d, child_node, FALSE);
772912Sartem }
782912Sartem
792912Sartem node = di_sibling_node (node);
802912Sartem } while ((node != DI_NODE_NIL) &&
812912Sartem (!is_root || di_parent_node (node) == root_node));
822912Sartem }
832912Sartem
842912Sartem void
devinfo_set_default_properties(HalDevice * d,HalDevice * parent,di_node_t node,char * devfs_path)852912Sartem devinfo_set_default_properties (HalDevice *d, HalDevice *parent, di_node_t node, char *devfs_path)
862912Sartem {
872912Sartem char *driver_name, *s;
882912Sartem const char *s1;
892912Sartem char udi[HAL_PATH_MAX];
902912Sartem
912912Sartem if (parent != NULL) {
923121Sartem hal_device_property_set_string (d, "info.parent", hal_device_get_udi (parent));
932912Sartem } else {
942912Sartem hal_device_property_set_string (d, "info.parent", "/org/freedesktop/Hal/devices/local");
952912Sartem }
962912Sartem
972912Sartem hal_util_compute_udi (hald_get_gdl (), udi, sizeof (udi),
982912Sartem "/org/freedesktop/Hal/devices%s_%d",
992912Sartem devfs_path,
1002912Sartem di_instance (node));
1012912Sartem hal_device_set_udi (d, udi);
1022912Sartem hal_device_property_set_string (d, "info.udi", udi);
1032912Sartem
1042912Sartem if (di_prop_lookup_strings (DDI_DEV_T_ANY, node, "model", &s) > 0) {
1052912Sartem hal_device_property_set_string (d, "info.product", s);
1062912Sartem } else {
1072912Sartem hal_device_property_set_string (d, "info.product", di_node_name (node));
1082912Sartem }
1092912Sartem
1102912Sartem hal_device_property_set_string (d, "solaris.devfs_path", devfs_path);
1112912Sartem
1122912Sartem if ((driver_name = di_driver_name (node)) != NULL) {
1132912Sartem hal_device_property_set_string (d, "info.solaris.driver",
1142912Sartem driver_name);
1152912Sartem }
1162912Sartem
1172912Sartem
1182912Sartem /* inherit parent's claim attributes */
1192912Sartem if (hal_device_property_get_bool (parent, "info.claimed")) {
1202912Sartem s1 = hal_device_property_get_string (parent, "info.claimed.service");
1212912Sartem if (s1 != NULL) {
1222912Sartem hal_device_property_set_bool (d, "info.claimed", TRUE);
1232912Sartem hal_device_property_set_string (d, "info.claimed.service", s1);
1242912Sartem }
1252912Sartem }
1262912Sartem }
1272912Sartem
1282912Sartem /* device handlers, ordered specific to generic */
1292912Sartem static DevinfoDevHandler *devinfo_handlers[] = {
1302912Sartem &devinfo_computer_handler,
1312912Sartem &devinfo_cpu_handler,
1322912Sartem &devinfo_ide_handler,
1332912Sartem &devinfo_scsi_handler,
1343121Sartem &devinfo_pcata_handler,
135*12426Sgdamore@opensolaris.org &devinfo_blkdev_handler,
1362912Sartem &devinfo_floppy_handler,
1372912Sartem &devinfo_usb_handler,
1382912Sartem &devinfo_ieee1394_handler,
1392912Sartem &devinfo_lofi_handler,
1404035Sphitran &devinfo_acpi_handler,
1416573Sphitran &devinfo_power_button_handler,
1426573Sphitran &devinfo_keyboard_handler,
1439608SLin.Guo@Sun.COM &devinfo_mouse_handler,
14410139SLin.Guo@Sun.COM &devinfo_pci_handler,
1452912Sartem &devinfo_default_handler,
1462912Sartem NULL
1472912Sartem };
1482912Sartem
1492912Sartem HalDevice *
devinfo_add_node(HalDevice * parent,di_node_t node)1502912Sartem devinfo_add_node(HalDevice *parent, di_node_t node)
1512912Sartem {
1522912Sartem HalDevice *d = NULL;
1532912Sartem char *devfs_path;
1542912Sartem char *device_type = NULL;
1552912Sartem DevinfoDevHandler *handler;
1562912Sartem int i;
1572912Sartem
1582912Sartem devfs_path = di_devfs_path (node);
1592912Sartem
1602912Sartem (void) di_prop_lookup_strings (DDI_DEV_T_ANY, node, "device_type",
1612912Sartem &device_type);
1622912Sartem
1632912Sartem for (i = 0; (d == NULL) && (devinfo_handlers[i] != NULL); i++) {
1642912Sartem handler = devinfo_handlers[i];
1652912Sartem d = handler->add (parent, node, devfs_path, device_type);
1662912Sartem }
1672912Sartem
1682912Sartem di_devfs_path_free(devfs_path);
1692912Sartem
1703121Sartem HAL_INFO (("add_node: %s", d ? hal_device_get_udi (d) : "none"));
1712912Sartem return (d);
1722912Sartem }
1732912Sartem
1742912Sartem void
devinfo_hotplug_enqueue(HalDevice * d,gchar * devfs_path,DevinfoDevHandler * handler,int action,int front)1752912Sartem devinfo_hotplug_enqueue(HalDevice *d, gchar *devfs_path, DevinfoDevHandler *handler, int action, int front)
1762912Sartem {
1772912Sartem HotplugEvent *hotplug_event;
1782912Sartem
1792912Sartem hotplug_event = g_new0 (HotplugEvent, 1);
1802912Sartem hotplug_event->action = action;
1812912Sartem hotplug_event->type = HOTPLUG_EVENT_DEVFS;
1822912Sartem hotplug_event->d = d;
1832912Sartem strlcpy (hotplug_event->un.devfs.devfs_path, devfs_path,
1842912Sartem sizeof (hotplug_event->un.devfs.devfs_path));
1852912Sartem hotplug_event->un.devfs.handler = handler;
1862912Sartem
1872912Sartem hotplug_event_enqueue (hotplug_event, front);
1882912Sartem }
1892912Sartem
1902912Sartem void
devinfo_add_enqueue(HalDevice * d,gchar * devfs_path,DevinfoDevHandler * handler)1912912Sartem devinfo_add_enqueue(HalDevice *d, gchar *devfs_path, DevinfoDevHandler *handler)
1922912Sartem {
1932912Sartem devinfo_hotplug_enqueue (d, devfs_path, handler, HOTPLUG_ACTION_ADD, 0);
1942912Sartem }
1952912Sartem
1962912Sartem void
devinfo_add_enqueue_at_front(HalDevice * d,gchar * devfs_path,DevinfoDevHandler * handler)1972912Sartem devinfo_add_enqueue_at_front(HalDevice *d, gchar *devfs_path, DevinfoDevHandler *handler)
1982912Sartem {
1992912Sartem devinfo_hotplug_enqueue (d, devfs_path, handler, HOTPLUG_ACTION_ADD, 1);
2002912Sartem }
2012912Sartem
2022912Sartem void
devinfo_remove_enqueue(gchar * devfs_path,DevinfoDevHandler * handler)2032912Sartem devinfo_remove_enqueue(gchar *devfs_path, DevinfoDevHandler *handler)
2042912Sartem {
2052912Sartem devinfo_hotplug_enqueue (NULL, devfs_path, handler, HOTPLUG_ACTION_REMOVE, 0);
2062912Sartem }
2072912Sartem
2082912Sartem void
devinfo_callouts_add_done(HalDevice * d,gpointer userdata1,gpointer userdata2)2092912Sartem devinfo_callouts_add_done (HalDevice *d, gpointer userdata1, gpointer userdata2)
2102912Sartem {
2112912Sartem void *end_token = (void *) userdata1;
2122912Sartem
2132912Sartem /* Move from temporary to global device store */
2142912Sartem hal_device_store_remove (hald_get_tdl (), d);
2152912Sartem hal_device_store_add (hald_get_gdl (), d);
2162912Sartem
2172912Sartem hotplug_event_end (end_token);
2182912Sartem }
2192912Sartem
2202912Sartem void
devinfo_callouts_probing_done(HalDevice * d,guint32 exit_type,gint return_code,char ** error,gpointer userdata1,gpointer userdata2)2212912Sartem devinfo_callouts_probing_done (HalDevice *d, guint32 exit_type, gint return_code, char **error, gpointer userdata1, gpointer userdata2)
2222912Sartem {
2232912Sartem void *end_token = (void *) userdata1;
2242912Sartem
2252912Sartem /* Discard device if probing reports failure */
2262912Sartem if (exit_type != HALD_RUN_SUCCESS || (return_code != 0)) {
2273121Sartem HAL_INFO (("Probing for %s failed %d", hal_device_get_udi (d), return_code));
2282912Sartem hal_device_store_remove (hald_get_tdl (), d);
2292912Sartem g_object_unref (d);
2302912Sartem hotplug_event_end (end_token);
2312912Sartem return;
2322912Sartem }
2332912Sartem
2342912Sartem /* Merge properties from .fdi files */
2352912Sartem di_search_and_merge (d, DEVICE_INFO_TYPE_INFORMATION);
2362912Sartem di_search_and_merge (d, DEVICE_INFO_TYPE_POLICY);
2372912Sartem
2382912Sartem hal_util_callout_device_add (d, devinfo_callouts_add_done, end_token, NULL);
2392912Sartem }
2402912Sartem
2412912Sartem void
devinfo_callouts_preprobing_done(HalDevice * d,gpointer userdata1,gpointer userdata2)2422912Sartem devinfo_callouts_preprobing_done (HalDevice *d, gpointer userdata1, gpointer userdata2)
2432912Sartem {
2442912Sartem void *end_token = (void *) userdata1;
2452912Sartem DevinfoDevHandler *handler = (DevinfoDevHandler *) userdata2;
2462912Sartem void (*probing_done) (HalDevice *, guint32, gint, char **, gpointer, gpointer);
2472912Sartem const gchar *prober;
2482912Sartem int prober_timeout;
2492912Sartem
2502912Sartem if (hal_device_property_get_bool (d, "info.ignore")) {
2512912Sartem HAL_INFO (("Preprobing merged info.ignore==TRUE"));
2522912Sartem
2532912Sartem /* Leave device with info.ignore==TRUE so we won't pick up children */
2542912Sartem hal_device_property_remove (d, "info.category");
2552912Sartem hal_device_property_remove (d, "info.capabilities");
2562912Sartem
2572912Sartem hal_device_store_remove (hald_get_tdl (), d);
2582912Sartem hal_device_store_add (hald_get_gdl (), d);
2592912Sartem
2602912Sartem hotplug_event_end (end_token);
2612912Sartem return;
2622912Sartem }
2632912Sartem
2642912Sartem if (handler != NULL && handler->get_prober != NULL) {
2652912Sartem prober = handler->get_prober (d, &prober_timeout);
2662912Sartem } else {
2672912Sartem prober = NULL;
2682912Sartem }
2692912Sartem
2702912Sartem if (handler->probing_done != NULL) {
2712912Sartem probing_done = handler->probing_done;
2722912Sartem } else {
2732912Sartem probing_done = devinfo_callouts_probing_done;
2742912Sartem }
2752912Sartem
2762912Sartem if (prober != NULL) {
2772912Sartem /* probe the device */
2783121Sartem HAL_INFO(("Probing udi=%s", hal_device_get_udi (d)));
2792912Sartem hald_runner_run (d,
2802912Sartem prober, NULL,
2812912Sartem prober_timeout,
2822912Sartem probing_done,
2832912Sartem (gpointer) end_token, (gpointer) handler);
2842912Sartem } else {
2852912Sartem probing_done (d, 0, 0, NULL, userdata1, userdata2);
2862912Sartem }
2872912Sartem }
2882912Sartem
2892912Sartem /* This is the beginning of hotplug even handling */
2902912Sartem void
hotplug_event_begin_add_devinfo(HalDevice * d,HalDevice * parent,DevinfoDevHandler * handler,void * end_token)2912912Sartem hotplug_event_begin_add_devinfo (HalDevice *d, HalDevice *parent, DevinfoDevHandler *handler, void *end_token)
2922912Sartem {
2938100SLin.Guo@Sun.COM HotplugEvent *hotplug_event = (HotplugEvent *)end_token;
2948100SLin.Guo@Sun.COM
2953121Sartem HAL_INFO(("Preprobing udi=%s", hal_device_get_udi (d)));
2962912Sartem
2978100SLin.Guo@Sun.COM if (parent == NULL && (strcmp(hotplug_event->un.devfs.devfs_path, "/") != 0)) {
2988100SLin.Guo@Sun.COM HAL_ERROR (("Parent is NULL, devfs_path=%s", hotplug_event->un.devfs.devfs_path));
2998100SLin.Guo@Sun.COM
3008100SLin.Guo@Sun.COM goto skip;
3018100SLin.Guo@Sun.COM }
3028100SLin.Guo@Sun.COM
3038100SLin.Guo@Sun.COM
3042912Sartem if (parent != NULL && hal_device_property_get_bool (parent, "info.ignore")) {
3052912Sartem HAL_INFO (("Ignoring device since parent has info.ignore==TRUE"));
3062912Sartem
3078100SLin.Guo@Sun.COM goto skip;
3082912Sartem }
3092912Sartem
3106112Sqz150045 if (hal_device_store_find (hald_get_tdl (), hal_device_get_udi (d)) == NULL) {
3116112Sqz150045
3126112Sqz150045 /* add to TDL so preprobing callouts and prober can access it */
3136112Sqz150045 hal_device_store_add (hald_get_tdl (), d);
3146112Sqz150045 }
3152912Sartem
3162912Sartem /* Process preprobe fdi files */
3172912Sartem di_search_and_merge (d, DEVICE_INFO_TYPE_PREPROBE);
3182912Sartem
3192912Sartem /* Run preprobe callouts */
3202912Sartem hal_util_callout_device_preprobe (d, devinfo_callouts_preprobing_done, end_token, handler);
3218100SLin.Guo@Sun.COM
3228100SLin.Guo@Sun.COM return;
3238100SLin.Guo@Sun.COM
3248100SLin.Guo@Sun.COM skip:
3258100SLin.Guo@Sun.COM if (hal_device_store_find (hald_get_tdl (), hal_device_get_udi (d)))
3268100SLin.Guo@Sun.COM hal_device_store_remove (hald_get_tdl (), d);
3278100SLin.Guo@Sun.COM
3288100SLin.Guo@Sun.COM g_object_unref (d);
3298100SLin.Guo@Sun.COM hotplug_event_end (end_token);
3308100SLin.Guo@Sun.COM
3318100SLin.Guo@Sun.COM return;
3322912Sartem }
3332912Sartem
3342912Sartem void
devinfo_remove(gchar * devfs_path)3352912Sartem devinfo_remove (gchar *devfs_path)
3362912Sartem {
3372912Sartem devinfo_remove_enqueue ((gchar *)devfs_path, NULL);
3382912Sartem }
3392912Sartem
3402912Sartem /* generate hotplug event for each device in this branch */
3412912Sartem void
devinfo_remove_branch(gchar * devfs_path,HalDevice * d)3422912Sartem devinfo_remove_branch (gchar *devfs_path, HalDevice *d)
3432912Sartem {
3442912Sartem GSList *i;
3452912Sartem GSList *children;
3462912Sartem HalDevice *child;
3472912Sartem char *child_devfs_path;
3482912Sartem
3492912Sartem if (d == NULL) {
3502912Sartem d = hal_device_store_match_key_value_string (hald_get_gdl (),
3512912Sartem "solaris.devfs_path", devfs_path);
3522912Sartem if (d == NULL)
3532912Sartem return;
3542912Sartem }
3552912Sartem
3563121Sartem HAL_INFO (("remove_branch: %s %s\n", devfs_path, hal_device_get_udi (d)));
3572912Sartem
3582912Sartem /* first remove children */
3592912Sartem children = hal_device_store_match_multiple_key_value_string (hald_get_gdl(),
3603121Sartem "info.parent", hal_device_get_udi (d));
3612912Sartem for (i = children; i != NULL; i = g_slist_next (i)) {
3622912Sartem child = HAL_DEVICE (i->data);
3633121Sartem HAL_INFO (("remove_branch: child %s\n", hal_device_get_udi (child)));
3642912Sartem devinfo_remove_branch ((gchar *)hal_device_property_get_string (child, "solaris.devfs_path"), child);
3652912Sartem }
3662912Sartem g_slist_free (children);
3672912Sartem HAL_INFO (("remove_branch: done with children"));
3682912Sartem
3692912Sartem /* then remove self */
3702912Sartem HAL_INFO (("remove_branch: queueing %s", devfs_path));
3712912Sartem devinfo_remove_enqueue (devfs_path, NULL);
3722912Sartem }
3732912Sartem
3742912Sartem void
devinfo_callouts_remove_done(HalDevice * d,gpointer userdata1,gpointer userdata2)3752912Sartem devinfo_callouts_remove_done (HalDevice *d, gpointer userdata1, gpointer userdata2)
3762912Sartem {
3772912Sartem void *end_token = (void *) userdata1;
3782912Sartem
3793121Sartem HAL_INFO (("Remove callouts completed udi=%s", hal_device_get_udi (d)));
3802912Sartem
3812912Sartem if (!hal_device_store_remove (hald_get_gdl (), d)) {
3822912Sartem HAL_WARNING (("Error removing device"));
3832912Sartem }
3842912Sartem g_object_unref (d);
3852912Sartem
3862912Sartem hotplug_event_end (end_token);
3872912Sartem }
3882912Sartem
3892912Sartem void
hotplug_event_begin_remove_devinfo(HalDevice * d,gchar * devfs_path,void * end_token)3902912Sartem hotplug_event_begin_remove_devinfo (HalDevice *d, gchar *devfs_path, void *end_token)
3912912Sartem {
3922912Sartem if (hal_device_has_capability (d, "volume")) {
3932912Sartem devinfo_volume_hotplug_begin_remove (d, devfs_path, end_token);
3942912Sartem } else {
3952912Sartem hal_util_callout_device_remove (d, devinfo_callouts_remove_done, end_token, NULL);
3962912Sartem }
3972912Sartem }
3982912Sartem
3992912Sartem gboolean
devinfo_device_rescan(HalDevice * d)4002912Sartem devinfo_device_rescan (HalDevice *d)
4012912Sartem {
4022912Sartem if (hal_device_has_capability (d, "block")) {
4032912Sartem return (devinfo_storage_device_rescan (d));
4047651SPhi.Tran@Sun.COM } else if (hal_device_has_capability (d, "button")) {
4057651SPhi.Tran@Sun.COM return (devinfo_lid_rescan (d));
4064035Sphitran } else {
4072912Sartem return (FALSE);
4082912Sartem }
4092912Sartem }
4103536Sjacobs
4113536Sjacobs static int
walk_devlinks(di_devlink_t devlink,void * arg)4123536Sjacobs walk_devlinks(di_devlink_t devlink, void *arg)
4133536Sjacobs {
4143536Sjacobs char **path= (char **)arg;
4153536Sjacobs
4163536Sjacobs *path = strdup(di_devlink_path(devlink));
4173536Sjacobs
4183536Sjacobs return (DI_WALK_TERMINATE);
4193536Sjacobs }
4203536Sjacobs
4213536Sjacobs char *
get_devlink(di_devlink_handle_t devlink_hdl,char * re,char * path)4223536Sjacobs get_devlink(di_devlink_handle_t devlink_hdl, char *re, char *path)
4233536Sjacobs {
4243536Sjacobs char *devlink_path = NULL;
4253536Sjacobs
4263536Sjacobs (void) di_devlink_walk(devlink_hdl, re, path,
4273536Sjacobs DI_PRIMARY_LINK, &devlink_path, walk_devlinks);
4283536Sjacobs
4293536Sjacobs return (devlink_path);
4303536Sjacobs }
431