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 HAL_INFO(("Preprobing udi=%s", hal_device_get_udi (d))); 293 294 if (parent != NULL && hal_device_property_get_bool (parent, "info.ignore")) { 295 HAL_INFO (("Ignoring device since parent has info.ignore==TRUE")); 296 297 if (hal_device_store_find (hald_get_tdl (), hal_device_get_udi (d))) 298 hal_device_store_remove (hald_get_tdl (), d); 299 300 hotplug_event_end (end_token); 301 return; 302 } 303 304 if (hal_device_store_find (hald_get_tdl (), hal_device_get_udi (d)) == NULL) { 305 306 /* add to TDL so preprobing callouts and prober can access it */ 307 hal_device_store_add (hald_get_tdl (), d); 308 } 309 310 /* Process preprobe fdi files */ 311 di_search_and_merge (d, DEVICE_INFO_TYPE_PREPROBE); 312 313 /* Run preprobe callouts */ 314 hal_util_callout_device_preprobe (d, devinfo_callouts_preprobing_done, end_token, handler); 315 } 316 317 void 318 devinfo_remove (gchar *devfs_path) 319 { 320 devinfo_remove_enqueue ((gchar *)devfs_path, NULL); 321 } 322 323 /* generate hotplug event for each device in this branch */ 324 void 325 devinfo_remove_branch (gchar *devfs_path, HalDevice *d) 326 { 327 GSList *i; 328 GSList *children; 329 HalDevice *child; 330 char *child_devfs_path; 331 332 if (d == NULL) { 333 d = hal_device_store_match_key_value_string (hald_get_gdl (), 334 "solaris.devfs_path", devfs_path); 335 if (d == NULL) 336 return; 337 } 338 339 HAL_INFO (("remove_branch: %s %s\n", devfs_path, hal_device_get_udi (d))); 340 341 /* first remove children */ 342 children = hal_device_store_match_multiple_key_value_string (hald_get_gdl(), 343 "info.parent", hal_device_get_udi (d)); 344 for (i = children; i != NULL; i = g_slist_next (i)) { 345 child = HAL_DEVICE (i->data); 346 HAL_INFO (("remove_branch: child %s\n", hal_device_get_udi (child))); 347 devinfo_remove_branch ((gchar *)hal_device_property_get_string (child, "solaris.devfs_path"), child); 348 } 349 g_slist_free (children); 350 HAL_INFO (("remove_branch: done with children")); 351 352 /* then remove self */ 353 HAL_INFO (("remove_branch: queueing %s", devfs_path)); 354 devinfo_remove_enqueue (devfs_path, NULL); 355 } 356 357 void 358 devinfo_callouts_remove_done (HalDevice *d, gpointer userdata1, gpointer userdata2) 359 { 360 void *end_token = (void *) userdata1; 361 362 HAL_INFO (("Remove callouts completed udi=%s", hal_device_get_udi (d))); 363 364 if (!hal_device_store_remove (hald_get_gdl (), d)) { 365 HAL_WARNING (("Error removing device")); 366 } 367 g_object_unref (d); 368 369 hotplug_event_end (end_token); 370 } 371 372 void 373 hotplug_event_begin_remove_devinfo (HalDevice *d, gchar *devfs_path, void *end_token) 374 { 375 if (hal_device_has_capability (d, "volume")) { 376 devinfo_volume_hotplug_begin_remove (d, devfs_path, end_token); 377 } else { 378 hal_util_callout_device_remove (d, devinfo_callouts_remove_done, end_token, NULL); 379 } 380 } 381 382 gboolean 383 devinfo_device_rescan (HalDevice *d) 384 { 385 if (hal_device_has_capability (d, "block")) { 386 return (devinfo_storage_device_rescan (d)); 387 } else if (hal_device_has_capability (d, "button")) { 388 return (devinfo_lid_rescan (d)); 389 } else { 390 return (FALSE); 391 } 392 } 393 394 static int 395 walk_devlinks(di_devlink_t devlink, void *arg) 396 { 397 char **path= (char **)arg; 398 399 *path = strdup(di_devlink_path(devlink)); 400 401 return (DI_WALK_TERMINATE); 402 } 403 404 char * 405 get_devlink(di_devlink_handle_t devlink_hdl, char *re, char *path) 406 { 407 char *devlink_path = NULL; 408 409 (void) di_devlink_walk(devlink_hdl, re, path, 410 DI_PRIMARY_LINK, &devlink_path, walk_devlinks); 411 412 return (devlink_path); 413 } 414 415