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