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