Home | History | Annotate | Download | only in solaris
      1 /***************************************************************************
      2  *
      3  * devinfo.c : main file for libdevinfo-based device enumeration
      4  *
      5  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
      6  * Use is subject to license terms.
      7  *
      8  * Licensed under the Academic Free License version 2.1
      9  *
     10  **************************************************************************/
     11 
     12 #ifdef HAVE_CONFIG_H
     13 #  include <config.h>
     14 #endif
     15 
     16 #include <stdio.h>
     17 #include <string.h>
     18 #include <libdevinfo.h>
     19 
     20 #include "../osspec.h"
     21 #include "../logger.h"
     22 #include "../hald.h"
     23 #include "../hald_dbus.h"
     24 #include "../device_info.h"
     25 #include "../util.h"
     26 #include "../hald_runner.h"
     27 #include "osspec_solaris.h"
     28 #include "hotplug.h"
     29 #include "devinfo.h"
     30 #include "devinfo_pci.h"
     31 #include "devinfo_storage.h"
     32 #include "devinfo_ieee1394.h"
     33 #include "devinfo_usb.h"
     34 #include "devinfo_misc.h"
     35 #include "devinfo_acpi.h"
     36 #include "devinfo_cpu.h"
     37 
     38 void devinfo_add_subtree(HalDevice *parent, di_node_t node, gboolean is_root);
     39 HalDevice *devinfo_add_node(HalDevice *parent, di_node_t node);
     40 
     41 void
     42 devinfo_add(HalDevice *parent, gchar *path)
     43 {
     44 	di_node_t	root;
     45 
     46 	if (strcmp (path, "/") == 0) {
     47 		if ((root = di_init(path, DINFOCACHE)) == DI_NODE_NIL) {
     48 			HAL_INFO (("di_init() failed %d", errno));
     49 			return;
     50 		}
     51 	} else {
     52 		if ((root = di_init(path, DINFOCPYALL)) == DI_NODE_NIL) {
     53 			HAL_INFO (("di_init() failed %d", errno));
     54 			return;
     55 		}
     56 	}
     57 
     58 	devinfo_add_subtree(parent, root, TRUE);
     59 
     60 	di_fini (root);
     61 }
     62 
     63 void
     64 devinfo_add_subtree(HalDevice *parent, di_node_t node, gboolean is_root)
     65 {
     66 	HalDevice *d;
     67 	di_node_t root_node, child_node;
     68 
     69 	HAL_INFO (("add_subtree: %s", di_node_name (node)));
     70 
     71 	root_node = node;
     72 	do {
     73 		d = devinfo_add_node (parent, node);
     74 
     75 		if ((d != NULL) &&
     76 		    (child_node = di_child_node (node)) != DI_NODE_NIL) {
     77 			devinfo_add_subtree (d, child_node, FALSE);
     78 		}
     79 
     80 		node = di_sibling_node (node);
     81 	} while ((node != DI_NODE_NIL) &&
     82 		(!is_root || di_parent_node (node) == root_node));
     83 }
     84 
     85 void
     86 devinfo_set_default_properties (HalDevice *d, HalDevice *parent, di_node_t node, char *devfs_path)
     87 {
     88 	char	*driver_name, *s;
     89 	const char *s1;
     90 	char	udi[HAL_PATH_MAX];
     91 
     92 	if (parent != NULL) {
     93 		hal_device_property_set_string (d, "info.parent", hal_device_get_udi (parent));
     94 	} else {
     95 		hal_device_property_set_string (d, "info.parent", "/org/freedesktop/Hal/devices/local");
     96 	}
     97 
     98 	hal_util_compute_udi (hald_get_gdl (), udi, sizeof (udi),
     99 				"/org/freedesktop/Hal/devices%s_%d",
    100 				devfs_path,
    101 				di_instance (node));
    102 	hal_device_set_udi (d, udi);
    103 	hal_device_property_set_string (d, "info.udi", udi);
    104 
    105 	if (di_prop_lookup_strings (DDI_DEV_T_ANY, node, "model", &s) > 0) {
    106 		hal_device_property_set_string (d, "info.product", s);
    107 	} else {
    108 		hal_device_property_set_string (d, "info.product", di_node_name (node));
    109 	}
    110 
    111 	hal_device_property_set_string (d, "solaris.devfs_path", devfs_path);
    112 
    113 	if ((driver_name = di_driver_name (node)) != NULL) {
    114 		hal_device_property_set_string (d, "info.solaris.driver",
    115 						driver_name);
    116 	}
    117 
    118 
    119 	/* inherit parent's claim attributes */
    120 	if (hal_device_property_get_bool (parent, "info.claimed")) {
    121 		s1 = hal_device_property_get_string (parent, "info.claimed.service");
    122 		if (s1 != NULL) {
    123 			hal_device_property_set_bool (d, "info.claimed", TRUE);
    124 			hal_device_property_set_string (d, "info.claimed.service", s1);
    125 		}
    126 	}
    127 }
    128 
    129 /* device handlers, ordered specific to generic */
    130 static DevinfoDevHandler *devinfo_handlers[] = {
    131 	&devinfo_computer_handler,
    132 	&devinfo_cpu_handler,
    133 	&devinfo_ide_handler,
    134 	&devinfo_scsi_handler,
    135 	&devinfo_pcata_handler,
    136 	&devinfo_floppy_handler,
    137 	&devinfo_usb_handler,
    138 	&devinfo_ieee1394_handler,
    139 	&devinfo_lofi_handler,
    140 	&devinfo_acpi_handler,
    141 	&devinfo_power_button_handler,
    142 	&devinfo_keyboard_handler,
    143 	&devinfo_mouse_handler,
    144 	&devinfo_pci_handler,
    145 	&devinfo_default_handler,
    146 	NULL
    147 };
    148 
    149 HalDevice *
    150 devinfo_add_node(HalDevice *parent, di_node_t node)
    151 {
    152 	HalDevice *d = NULL;
    153 	char	*devfs_path;
    154 	char	*device_type = NULL;
    155 	DevinfoDevHandler *handler;
    156 	int	i;
    157 
    158 	devfs_path = di_devfs_path (node);
    159 
    160         (void) di_prop_lookup_strings (DDI_DEV_T_ANY, node, "device_type",
    161 	    &device_type);
    162 
    163 	for (i = 0; (d == NULL) && (devinfo_handlers[i] != NULL); i++) {
    164 		handler = devinfo_handlers[i];
    165 		d = handler->add (parent, node, devfs_path, device_type);
    166 	}
    167 
    168 	di_devfs_path_free(devfs_path);
    169 
    170 	HAL_INFO (("add_node: %s", d ? hal_device_get_udi (d) : "none"));
    171 	return (d);
    172 }
    173 
    174 void
    175 devinfo_hotplug_enqueue(HalDevice *d, gchar *devfs_path, DevinfoDevHandler *handler, int action, int front)
    176 {
    177 	HotplugEvent *hotplug_event;
    178 
    179 	hotplug_event = g_new0 (HotplugEvent, 1);
    180 	hotplug_event->action = action;
    181 	hotplug_event->type = HOTPLUG_EVENT_DEVFS;
    182 	hotplug_event->d = d;
    183 	strlcpy (hotplug_event->un.devfs.devfs_path, devfs_path,
    184 		sizeof (hotplug_event->un.devfs.devfs_path));
    185 	hotplug_event->un.devfs.handler = handler;
    186 
    187 	hotplug_event_enqueue (hotplug_event, front);
    188 }
    189 
    190 void
    191 devinfo_add_enqueue(HalDevice *d, gchar *devfs_path, DevinfoDevHandler *handler)
    192 {
    193 	devinfo_hotplug_enqueue (d, devfs_path, handler, HOTPLUG_ACTION_ADD, 0);
    194 }
    195 
    196 void
    197 devinfo_add_enqueue_at_front(HalDevice *d, gchar *devfs_path, DevinfoDevHandler *handler)
    198 {
    199 	devinfo_hotplug_enqueue (d, devfs_path, handler, HOTPLUG_ACTION_ADD, 1);
    200 }
    201 
    202 void
    203 devinfo_remove_enqueue(gchar *devfs_path, DevinfoDevHandler *handler)
    204 {
    205 	devinfo_hotplug_enqueue (NULL, devfs_path, handler, HOTPLUG_ACTION_REMOVE, 0);
    206 }
    207 
    208 void
    209 devinfo_callouts_add_done (HalDevice *d, gpointer userdata1, gpointer userdata2)
    210 {
    211         void *end_token = (void *) userdata1;
    212 
    213         /* Move from temporary to global device store */
    214         hal_device_store_remove (hald_get_tdl (), d);
    215         hal_device_store_add (hald_get_gdl (), d);
    216 
    217         hotplug_event_end (end_token);
    218 }
    219 
    220 void
    221 devinfo_callouts_probing_done (HalDevice *d, guint32 exit_type, gint return_code, char **error, gpointer userdata1, gpointer userdata2)
    222 {
    223         void *end_token = (void *) userdata1;
    224 
    225         /* Discard device if probing reports failure */
    226         if (exit_type != HALD_RUN_SUCCESS || (return_code != 0)) {
    227 		HAL_INFO (("Probing for %s failed %d", hal_device_get_udi (d), return_code));
    228                 hal_device_store_remove (hald_get_tdl (), d);
    229                 g_object_unref (d);
    230                 hotplug_event_end (end_token);
    231 		return;
    232         }
    233 
    234         /* Merge properties from .fdi files */
    235         di_search_and_merge (d, DEVICE_INFO_TYPE_INFORMATION);
    236         di_search_and_merge (d, DEVICE_INFO_TYPE_POLICY);
    237 
    238 	hal_util_callout_device_add (d, devinfo_callouts_add_done, end_token, NULL);
    239 }
    240 
    241 void
    242 devinfo_callouts_preprobing_done (HalDevice *d, gpointer userdata1, gpointer userdata2)
    243 {
    244         void *end_token = (void *) userdata1;
    245 	DevinfoDevHandler *handler = (DevinfoDevHandler *) userdata2;
    246 	void (*probing_done) (HalDevice *, guint32, gint, char **, gpointer, gpointer);
    247 	const gchar *prober;
    248 	int prober_timeout;
    249 
    250         if (hal_device_property_get_bool (d, "info.ignore")) {
    251 		HAL_INFO (("Preprobing merged info.ignore==TRUE"));
    252 
    253                 /* Leave device with info.ignore==TRUE so we won't pick up children */
    254 		hal_device_property_remove (d, "info.category");
    255 		hal_device_property_remove (d, "info.capabilities");
    256 
    257 		hal_device_store_remove (hald_get_tdl (), d);
    258 		hal_device_store_add (hald_get_gdl (), d);
    259 
    260 		hotplug_event_end (end_token);
    261 		return;
    262         }
    263 
    264         if (handler != NULL && handler->get_prober != NULL) {
    265                 prober = handler->get_prober (d, &prober_timeout);
    266         } else {
    267                 prober = NULL;
    268 	}
    269 
    270 	if (handler->probing_done != NULL) {
    271 		probing_done = handler->probing_done;
    272 	} else {
    273 		probing_done = devinfo_callouts_probing_done;
    274 	}
    275 
    276         if (prober != NULL) {
    277                 /* probe the device */
    278 		HAL_INFO(("Probing udi=%s", hal_device_get_udi (d)));
    279                 hald_runner_run (d,
    280 				prober, NULL,
    281 				prober_timeout,
    282 				probing_done,
    283 				(gpointer) end_token, (gpointer) handler);
    284 	} else {
    285 		probing_done (d, 0, 0, NULL, userdata1, userdata2);
    286 	}
    287 }
    288 
    289 /* This is the beginning of hotplug even handling */
    290 void
    291 hotplug_event_begin_add_devinfo (HalDevice *d, HalDevice *parent, DevinfoDevHandler *handler, void *end_token)
    292 {
    293 	HotplugEvent *hotplug_event = (HotplugEvent *)end_token;
    294 
    295 	HAL_INFO(("Preprobing udi=%s", hal_device_get_udi (d)));
    296 
    297 	if (parent == NULL && (strcmp(hotplug_event->un.devfs.devfs_path, "/") != 0)) {
    298 		HAL_ERROR (("Parent is NULL, devfs_path=%s", hotplug_event->un.devfs.devfs_path));
    299 
    300 		goto skip;
    301 	}
    302 
    303 
    304 	if (parent != NULL && hal_device_property_get_bool (parent, "info.ignore")) {
    305 		HAL_INFO (("Ignoring device since parent has info.ignore==TRUE"));
    306 
    307 		goto skip;
    308 	}
    309 
    310 	if (hal_device_store_find (hald_get_tdl (), hal_device_get_udi (d)) == NULL) {
    311 
    312 		/* add to TDL so preprobing callouts and prober can access it */
    313 		hal_device_store_add (hald_get_tdl (), d);
    314 	}
    315 
    316         /* Process preprobe fdi files */
    317         di_search_and_merge (d, DEVICE_INFO_TYPE_PREPROBE);
    318 
    319         /* Run preprobe callouts */
    320         hal_util_callout_device_preprobe (d, devinfo_callouts_preprobing_done, end_token, handler);
    321 
    322 	return;
    323 
    324 skip:
    325 	if (hal_device_store_find (hald_get_tdl (), hal_device_get_udi (d)))
    326 		hal_device_store_remove (hald_get_tdl (), d);
    327 
    328 	g_object_unref (d);
    329 	hotplug_event_end (end_token);
    330 
    331 	return;
    332 }
    333 
    334 void
    335 devinfo_remove (gchar *devfs_path)
    336 {
    337 	devinfo_remove_enqueue ((gchar *)devfs_path, NULL);
    338 }
    339 
    340 /* generate hotplug event for each device in this branch */
    341 void
    342 devinfo_remove_branch (gchar *devfs_path, HalDevice *d)
    343 {
    344 	GSList *i;
    345 	GSList *children;
    346 	HalDevice *child;
    347 	char *child_devfs_path;
    348 
    349 	if (d == NULL) {
    350 		d = hal_device_store_match_key_value_string (hald_get_gdl (),
    351 			"solaris.devfs_path", devfs_path);
    352 		if (d == NULL)
    353 			return;
    354 	}
    355 
    356 	HAL_INFO (("remove_branch: %s %s\n", devfs_path, hal_device_get_udi (d)));
    357 
    358 	/* first remove children */
    359 	children = hal_device_store_match_multiple_key_value_string (hald_get_gdl(),
    360 		"info.parent", hal_device_get_udi (d));
    361         for (i = children; i != NULL; i = g_slist_next (i)) {
    362                 child = HAL_DEVICE (i->data);
    363 		HAL_INFO (("remove_branch: child %s\n", hal_device_get_udi (child)));
    364 		devinfo_remove_branch ((gchar *)hal_device_property_get_string (child, "solaris.devfs_path"), child);
    365 	}
    366 	g_slist_free (children);
    367 	HAL_INFO (("remove_branch: done with children"));
    368 
    369 	/* then remove self */
    370 	HAL_INFO (("remove_branch: queueing %s", devfs_path));
    371 	devinfo_remove_enqueue (devfs_path, NULL);
    372 }
    373 
    374 void
    375 devinfo_callouts_remove_done (HalDevice *d, gpointer userdata1, gpointer userdata2)
    376 {
    377         void *end_token = (void *) userdata1;
    378 
    379         HAL_INFO (("Remove callouts completed udi=%s", hal_device_get_udi (d)));
    380 
    381         if (!hal_device_store_remove (hald_get_gdl (), d)) {
    382                 HAL_WARNING (("Error removing device"));
    383         }
    384         g_object_unref (d);
    385 
    386         hotplug_event_end (end_token);
    387 }
    388 
    389 void
    390 hotplug_event_begin_remove_devinfo (HalDevice *d, gchar *devfs_path, void *end_token)
    391 {
    392 	if (hal_device_has_capability (d, "volume")) {
    393 		devinfo_volume_hotplug_begin_remove (d, devfs_path, end_token);
    394 	} else {
    395 		hal_util_callout_device_remove (d, devinfo_callouts_remove_done, end_token, NULL);
    396 	}
    397 }
    398 
    399 gboolean
    400 devinfo_device_rescan (HalDevice *d)
    401 {
    402 	if (hal_device_has_capability (d, "block")) {
    403 		return (devinfo_storage_device_rescan (d));
    404 	} else if (hal_device_has_capability (d, "button")) {
    405 		return (devinfo_lid_rescan (d));
    406         } else {
    407 		return (FALSE);
    408 	}
    409 }
    410 
    411 static int
    412 walk_devlinks(di_devlink_t devlink, void *arg)
    413 {
    414         char    **path= (char **)arg;
    415 
    416         *path = strdup(di_devlink_path(devlink));
    417 
    418         return (DI_WALK_TERMINATE);
    419 }
    420 
    421 char *
    422 get_devlink(di_devlink_handle_t devlink_hdl, char *re, char *path)
    423 {
    424         char    *devlink_path = NULL;
    425 
    426         (void) di_devlink_walk(devlink_hdl, re, path,
    427             DI_PRIMARY_LINK, &devlink_path, walk_devlinks);
    428 
    429         return (devlink_path);
    430 }
    431