xref: /illumos-gate/usr/src/cmd/hal/hald/solaris/devinfo.c (revision 8b80e8cb)
1 /***************************************************************************
2  *
3  * devinfo.c : main file for libdevinfo-based device enumeration
4  *
5  * Copyright 2008 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_pci_handler,
140 	&devinfo_lofi_handler,
141 	&devinfo_acpi_handler,
142 	&devinfo_power_button_handler,
143 	&devinfo_keyboard_handler,
144 	&devinfo_default_handler,
145 	NULL
146 };
147 
148 HalDevice *
149 devinfo_add_node(HalDevice *parent, di_node_t node)
150 {
151 	HalDevice *d = NULL;
152 	char	*devfs_path;
153 	char	*device_type = NULL;
154 	DevinfoDevHandler *handler;
155 	int	i;
156 
157 	devfs_path = di_devfs_path (node);
158 
159         (void) di_prop_lookup_strings (DDI_DEV_T_ANY, node, "device_type",
160 	    &device_type);
161 
162 	for (i = 0; (d == NULL) && (devinfo_handlers[i] != NULL); i++) {
163 		handler = devinfo_handlers[i];
164 		d = handler->add (parent, node, devfs_path, device_type);
165 	}
166 
167 	di_devfs_path_free(devfs_path);
168 
169 	HAL_INFO (("add_node: %s", d ? hal_device_get_udi (d) : "none"));
170 	return (d);
171 }
172 
173 void
174 devinfo_hotplug_enqueue(HalDevice *d, gchar *devfs_path, DevinfoDevHandler *handler, int action, int front)
175 {
176 	HotplugEvent *hotplug_event;
177 
178 	hotplug_event = g_new0 (HotplugEvent, 1);
179 	hotplug_event->action = action;
180 	hotplug_event->type = HOTPLUG_EVENT_DEVFS;
181 	hotplug_event->d = d;
182 	strlcpy (hotplug_event->un.devfs.devfs_path, devfs_path,
183 		sizeof (hotplug_event->un.devfs.devfs_path));
184 	hotplug_event->un.devfs.handler = handler;
185 
186 	hotplug_event_enqueue (hotplug_event, front);
187 }
188 
189 void
190 devinfo_add_enqueue(HalDevice *d, gchar *devfs_path, DevinfoDevHandler *handler)
191 {
192 	devinfo_hotplug_enqueue (d, devfs_path, handler, HOTPLUG_ACTION_ADD, 0);
193 }
194 
195 void
196 devinfo_add_enqueue_at_front(HalDevice *d, gchar *devfs_path, DevinfoDevHandler *handler)
197 {
198 	devinfo_hotplug_enqueue (d, devfs_path, handler, HOTPLUG_ACTION_ADD, 1);
199 }
200 
201 void
202 devinfo_remove_enqueue(gchar *devfs_path, DevinfoDevHandler *handler)
203 {
204 	devinfo_hotplug_enqueue (NULL, devfs_path, handler, HOTPLUG_ACTION_REMOVE, 0);
205 }
206 
207 void
208 devinfo_callouts_add_done (HalDevice *d, gpointer userdata1, gpointer userdata2)
209 {
210         void *end_token = (void *) userdata1;
211 
212         /* Move from temporary to global device store */
213         hal_device_store_remove (hald_get_tdl (), d);
214         hal_device_store_add (hald_get_gdl (), d);
215 
216         hotplug_event_end (end_token);
217 }
218 
219 void
220 devinfo_callouts_probing_done (HalDevice *d, guint32 exit_type, gint return_code, char **error, gpointer userdata1, gpointer userdata2)
221 {
222         void *end_token = (void *) userdata1;
223 
224         /* Discard device if probing reports failure */
225         if (exit_type != HALD_RUN_SUCCESS || (return_code != 0)) {
226 		HAL_INFO (("Probing for %s failed %d", hal_device_get_udi (d), return_code));
227                 hal_device_store_remove (hald_get_tdl (), d);
228                 g_object_unref (d);
229                 hotplug_event_end (end_token);
230 		return;
231         }
232 
233         /* Merge properties from .fdi files */
234         di_search_and_merge (d, DEVICE_INFO_TYPE_INFORMATION);
235         di_search_and_merge (d, DEVICE_INFO_TYPE_POLICY);
236 
237 	hal_util_callout_device_add (d, devinfo_callouts_add_done, end_token, NULL);
238 }
239 
240 void
241 devinfo_callouts_preprobing_done (HalDevice *d, gpointer userdata1, gpointer userdata2)
242 {
243         void *end_token = (void *) userdata1;
244 	DevinfoDevHandler *handler = (DevinfoDevHandler *) userdata2;
245 	void (*probing_done) (HalDevice *, guint32, gint, char **, gpointer, gpointer);
246 	const gchar *prober;
247 	int prober_timeout;
248 
249         if (hal_device_property_get_bool (d, "info.ignore")) {
250 		HAL_INFO (("Preprobing merged info.ignore==TRUE"));
251 
252                 /* Leave device with info.ignore==TRUE so we won't pick up children */
253 		hal_device_property_remove (d, "info.category");
254 		hal_device_property_remove (d, "info.capabilities");
255 
256 		hal_device_store_remove (hald_get_tdl (), d);
257 		hal_device_store_add (hald_get_gdl (), d);
258 
259 		hotplug_event_end (end_token);
260 		return;
261         }
262 
263         if (handler != NULL && handler->get_prober != NULL) {
264                 prober = handler->get_prober (d, &prober_timeout);
265         } else {
266                 prober = NULL;
267 	}
268 
269 	if (handler->probing_done != NULL) {
270 		probing_done = handler->probing_done;
271 	} else {
272 		probing_done = devinfo_callouts_probing_done;
273 	}
274 
275         if (prober != NULL) {
276                 /* probe the device */
277 		HAL_INFO(("Probing udi=%s", hal_device_get_udi (d)));
278                 hald_runner_run (d,
279 				prober, NULL,
280 				prober_timeout,
281 				probing_done,
282 				(gpointer) end_token, (gpointer) handler);
283 	} else {
284 		probing_done (d, 0, 0, NULL, userdata1, userdata2);
285 	}
286 }
287 
288 /* This is the beginning of hotplug even handling */
289 void
290 hotplug_event_begin_add_devinfo (HalDevice *d, HalDevice *parent, DevinfoDevHandler *handler, void *end_token)
291 {
292 	HotplugEvent *hotplug_event = (HotplugEvent *)end_token;
293 
294 	HAL_INFO(("Preprobing udi=%s", hal_device_get_udi (d)));
295 
296 	if (parent == NULL && (strcmp(hotplug_event->un.devfs.devfs_path, "/") != 0)) {
297 		HAL_ERROR (("Parent is NULL, devfs_path=%s", hotplug_event->un.devfs.devfs_path));
298 
299 		goto skip;
300 	}
301 
302 
303 	if (parent != NULL && hal_device_property_get_bool (parent, "info.ignore")) {
304 		HAL_INFO (("Ignoring device since parent has info.ignore==TRUE"));
305 
306 		goto skip;
307 	}
308 
309 	if (hal_device_store_find (hald_get_tdl (), hal_device_get_udi (d)) == NULL) {
310 
311 		/* add to TDL so preprobing callouts and prober can access it */
312 		hal_device_store_add (hald_get_tdl (), d);
313 	}
314 
315         /* Process preprobe fdi files */
316         di_search_and_merge (d, DEVICE_INFO_TYPE_PREPROBE);
317 
318         /* Run preprobe callouts */
319         hal_util_callout_device_preprobe (d, devinfo_callouts_preprobing_done, end_token, handler);
320 
321 	return;
322 
323 skip:
324 	if (hal_device_store_find (hald_get_tdl (), hal_device_get_udi (d)))
325 		hal_device_store_remove (hald_get_tdl (), d);
326 
327 	g_object_unref (d);
328 	hotplug_event_end (end_token);
329 
330 	return;
331 }
332 
333 void
334 devinfo_remove (gchar *devfs_path)
335 {
336 	devinfo_remove_enqueue ((gchar *)devfs_path, NULL);
337 }
338 
339 /* generate hotplug event for each device in this branch */
340 void
341 devinfo_remove_branch (gchar *devfs_path, HalDevice *d)
342 {
343 	GSList *i;
344 	GSList *children;
345 	HalDevice *child;
346 	char *child_devfs_path;
347 
348 	if (d == NULL) {
349 		d = hal_device_store_match_key_value_string (hald_get_gdl (),
350 			"solaris.devfs_path", devfs_path);
351 		if (d == NULL)
352 			return;
353 	}
354 
355 	HAL_INFO (("remove_branch: %s %s\n", devfs_path, hal_device_get_udi (d)));
356 
357 	/* first remove children */
358 	children = hal_device_store_match_multiple_key_value_string (hald_get_gdl(),
359 		"info.parent", hal_device_get_udi (d));
360         for (i = children; i != NULL; i = g_slist_next (i)) {
361                 child = HAL_DEVICE (i->data);
362 		HAL_INFO (("remove_branch: child %s\n", hal_device_get_udi (child)));
363 		devinfo_remove_branch ((gchar *)hal_device_property_get_string (child, "solaris.devfs_path"), child);
364 	}
365 	g_slist_free (children);
366 	HAL_INFO (("remove_branch: done with children"));
367 
368 	/* then remove self */
369 	HAL_INFO (("remove_branch: queueing %s", devfs_path));
370 	devinfo_remove_enqueue (devfs_path, NULL);
371 }
372 
373 void
374 devinfo_callouts_remove_done (HalDevice *d, gpointer userdata1, gpointer userdata2)
375 {
376         void *end_token = (void *) userdata1;
377 
378         HAL_INFO (("Remove callouts completed udi=%s", hal_device_get_udi (d)));
379 
380         if (!hal_device_store_remove (hald_get_gdl (), d)) {
381                 HAL_WARNING (("Error removing device"));
382         }
383         g_object_unref (d);
384 
385         hotplug_event_end (end_token);
386 }
387 
388 void
389 hotplug_event_begin_remove_devinfo (HalDevice *d, gchar *devfs_path, void *end_token)
390 {
391 	if (hal_device_has_capability (d, "volume")) {
392 		devinfo_volume_hotplug_begin_remove (d, devfs_path, end_token);
393 	} else {
394 		hal_util_callout_device_remove (d, devinfo_callouts_remove_done, end_token, NULL);
395 	}
396 }
397 
398 gboolean
399 devinfo_device_rescan (HalDevice *d)
400 {
401 	if (hal_device_has_capability (d, "block")) {
402 		return (devinfo_storage_device_rescan (d));
403 	} else if (hal_device_has_capability (d, "button")) {
404 		return (devinfo_lid_rescan (d));
405         } else {
406 		return (FALSE);
407 	}
408 }
409 
410 static int
411 walk_devlinks(di_devlink_t devlink, void *arg)
412 {
413         char    **path= (char **)arg;
414 
415         *path = strdup(di_devlink_path(devlink));
416 
417         return (DI_WALK_TERMINATE);
418 }
419 
420 char *
421 get_devlink(di_devlink_handle_t devlink_hdl, char *re, char *path)
422 {
423         char    *devlink_path = NULL;
424 
425         (void) di_devlink_walk(devlink_hdl, re, path,
426             DI_PRIMARY_LINK, &devlink_path, walk_devlinks);
427 
428         return (devlink_path);
429 }
430 
431