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