1 /***************************************************************************
3 * sysevent.c : Solaris sysevents
5 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
6 * Use is subject to license terms.
8 * Licensed under the Academic Free License version 2.1
10 **************************************************************************/
24 #include <libdevinfo.h>
25 #include <libsysevent.h>
26 #include <sys/sysevent/dev.h>
27 #include <sys/sysevent/pwrctl.h>
28 #include <sys/sysevent/dr.h>
30 #include <config_admin.h>
33 #include "../osspec.h"
34 #include "../logger.h"
36 #include "../hald_dbus.h"
37 #include "../device_info.h"
39 #include "osspec_solaris.h"
42 #include "devinfo_storage.h"
43 #include "devinfo_acpi.h"
44 #include "devinfo_usb.h"
46 #include "devinfo_misc.h"
47 #include "devinfo_cpu.h"
50 #define ESC_LOFI "lofi"
53 static void sysevent_dev_handler(sysevent_t
*);
54 static gboolean
sysevent_iochannel_data(GIOChannel
*, GIOCondition
, gpointer
);
55 static void sysevent_dev_add(gchar
*, gchar
*);
56 static void sysevent_dev_remove(gchar
*, gchar
*);
57 static void sysevent_dev_branch(gchar
*);
58 static void sysevent_lofi_add(gchar
*, gchar
*);
59 static void sysevent_lofi_remove(gchar
*, gchar
*);
60 static void sysevent_devfs_add(gchar
*);
61 static void sysevent_pwrctl(gchar
*, gchar
*, gchar
*, gchar
*, gchar
*,
63 static void sysevent_process_dr(gchar
*, gchar
*);
65 static sysevent_handle_t
*shp
;
67 static int sysevent_pipe_fds
[2];
68 static GIOChannel
*sysevent_iochannel
;
69 static guint sysevent_source_id
;
78 * pipe used to serialize sysevents through the main loop
80 if (pipe (sysevent_pipe_fds
) != 0) {
81 HAL_INFO (("pipe() failed errno=%d", errno
));
84 sysevent_iochannel
= g_io_channel_unix_new (sysevent_pipe_fds
[0]);
85 if (sysevent_iochannel
== NULL
) {
86 HAL_INFO (("g_io_channel_unix_new failed"));
89 g_io_channel_set_flags (sysevent_iochannel
, G_IO_FLAG_NONBLOCK
, &err
);
90 sysevent_source_id
= g_io_add_watch (
91 sysevent_iochannel
, G_IO_IN
, sysevent_iochannel_data
, NULL
);
93 shp
= sysevent_bind_handle(sysevent_dev_handler
);
95 HAL_INFO (("sysevent_bind_handle failed %d", errno
));
101 subcl
[2] = ESC_PRINTER
;
102 if (sysevent_subscribe_event(shp
, EC_DEV_ADD
, subcl
, 3) != 0) {
103 HAL_INFO (("subscribe(dev_add) failed %d", errno
));
104 sysevent_unbind_handle(shp
);
107 if (sysevent_subscribe_event(shp
, EC_DEV_REMOVE
, subcl
, 3) != 0) {
108 HAL_INFO (("subscribe(dev_remove) failed %d", errno
));
109 sysevent_unbind_handle(shp
);
113 subcl
[0] = ESC_DEV_BRANCH_REMOVE
;
114 if (sysevent_subscribe_event(shp
, EC_DEV_BRANCH
, subcl
, 1) != 0) {
115 HAL_INFO (("subscribe(dev_branch) failed %d", errno
));
116 sysevent_unbind_handle(shp
);
120 subcl
[0] = ESC_PWRCTL_ADD
;
121 subcl
[1] = ESC_PWRCTL_REMOVE
;
122 subcl
[2] = ESC_PWRCTL_STATE_CHANGE
;
123 subcl
[3] = ESC_PWRCTL_BRIGHTNESS_UP
;
124 subcl
[4] = ESC_PWRCTL_BRIGHTNESS_DOWN
;
125 subcl
[5] = ESC_PWRCTL_POWER_BUTTON
;
126 if (sysevent_subscribe_event(shp
, EC_PWRCTL
, subcl
, 6) != 0) {
127 HAL_INFO(("subscribe(dev_add) failed %d", errno
));
128 sysevent_unbind_handle(shp
);
132 subcl
[0] = ESC_DEVFS_DEVI_ADD
;
133 if (sysevent_subscribe_event(shp
, EC_DEVFS
, subcl
, 1) != 0) {
134 HAL_INFO (("subscribe(EC_DEVFS) failed %d", errno
));
135 sysevent_unbind_handle(shp
);
139 subcl
[0] = ESC_DR_AP_STATE_CHANGE
;
140 if (sysevent_subscribe_event(shp
, EC_DR
, subcl
, 1) != 0) {
141 HAL_INFO (("subscribe(dynamic reconfiguration) failed %d",
143 sysevent_unbind_handle(shp
);
153 sysevent_unbind_handle(shp
);
158 sysevent_dev_handler(sysevent_t
*ev
)
171 if ((class = sysevent_get_class_name(ev
)) == NULL
)
174 if ((subclass
= sysevent_get_subclass_name(ev
)) == NULL
)
177 if (sysevent_get_attr_list(ev
, &attr_list
) != 0)
180 if (strcmp(class, EC_DEVFS
) == 0) {
181 if (nvlist_lookup_string(attr_list
, DEVFS_PATHNAME
, &phys_path
) != 0) {
185 snprintf(s
, sizeof (s
), "%s %s %s\n",
186 class, subclass
, phys_path
);
187 nwritten
= write(sysevent_pipe_fds
[1], s
, strlen(s
) + 1);
189 HAL_INFO (("sysevent_dev_handler: wrote %d bytes", nwritten
));
193 if (strcmp(class, EC_PWRCTL
) == 0) {
194 if (nvlist_lookup_string(attr_list
, PWRCTL_DEV_PHYS_PATH
,
198 } else if (strcmp(class, EC_DR
) == 0) {
199 if (nvlist_lookup_string(attr_list
, DR_AP_ID
,
203 } else if (nvlist_lookup_string(attr_list
, DEV_PHYS_PATH
, &phys_path
)
209 * In case of EC_DR, use dev_name to store DR_HINT val
211 if (strcmp(class, EC_DR
) == 0) {
212 if (nvlist_lookup_string(attr_list
, DR_HINT
, &dev_name
) != 0) {
215 } else if (nvlist_lookup_string(attr_list
, DEV_NAME
, &dev_name
) != 0) {
216 if (strcmp(class, EC_PWRCTL
) == 0) {
223 if (nvlist_lookup_string(attr_list
, PWRCTL_DEV_HID
, &dev_hid
) != 0) {
226 if (nvlist_lookup_string(attr_list
, PWRCTL_DEV_UID
, &dev_uid
) != 0) {
229 if (nvlist_lookup_uint32(attr_list
, PWRCTL_DEV_INDEX
, &dev_index
)
234 snprintf(s
, sizeof (s
), "%s %s %s %s %s %s %d\n",
235 class, subclass
, phys_path
, dev_name
, dev_hid
, dev_uid
, dev_index
);
236 nwritten
= write(sysevent_pipe_fds
[1], s
, strlen(s
) + 1);
238 HAL_INFO (("sysevent_dev_handler: wrote %d bytes", nwritten
));
241 nvlist_free(attr_list
);
245 sysevent_iochannel_data (GIOChannel
*source
,
246 GIOCondition condition
,
254 gchar subclass
[1024];
255 gchar phys_path
[1024];
256 gchar dev_name
[1024];
261 HAL_INFO (("sysevent_iochannel_data"));
263 while (g_io_channel_read_line (sysevent_iochannel
, &s
, &len
, NULL
,
264 &err
) == G_IO_STATUS_NORMAL
) {
268 HAL_INFO (("IOChannel val => %s", s
));
269 class[0] = subclass
[0] = phys_path
[0] = dev_name
[0] =
270 dev_hid
[0] = dev_uid
[0] = '\0';
271 matches
= sscanf(s
, "%s %s %s %s %s %s %d", class, subclass
,
272 phys_path
, dev_name
, dev_hid
, dev_uid
, &dev_index
);
278 HAL_INFO (("sysevent: class=%s, sub=%s", class, subclass
));
280 if (strcmp(class, EC_DEV_ADD
) == 0) {
281 if ((strcmp(subclass
, ESC_DISK
) == 0) ||
282 (strcmp(subclass
, ESC_PRINTER
) == 0)) {
283 sysevent_dev_add(phys_path
, dev_name
);
284 } else if (strcmp(subclass
, ESC_LOFI
) == 0) {
285 sysevent_lofi_add(phys_path
, dev_name
);
287 } else if (strcmp(class, EC_DEV_REMOVE
) == 0) {
288 if ((strcmp(subclass
, ESC_DISK
) == 0) ||
289 (strcmp(subclass
, ESC_PRINTER
) == 0)) {
290 sysevent_dev_remove(phys_path
, dev_name
);
291 } else if (strcmp(subclass
, ESC_LOFI
) == 0) {
292 sysevent_lofi_remove(phys_path
, dev_name
);
294 } else if (strcmp(class, EC_DEV_BRANCH
) == 0) {
295 sysevent_dev_branch(phys_path
);
296 } else if (strcmp(class, EC_PWRCTL
) == 0) {
297 sysevent_pwrctl(class, subclass
, phys_path
,
298 dev_name
, dev_hid
, dev_uid
, dev_index
);
299 } else if (strcmp(class, EC_DEVFS
) == 0) {
300 if (strcmp(subclass
, ESC_DEVFS_DEVI_ADD
) == 0) {
301 sysevent_devfs_add(phys_path
);
303 } else if (strcmp(class, EC_DR
) == 0) {
305 * Note: AP_ID is stored in phys_path and HINT is
306 * stored in dev_name, to avoid creating seperate
307 * variables and multiple conditions checking
309 HAL_DEBUG (("In %s, AP_ID-> %s, Hint-> %s", class,
310 phys_path
, dev_name
));
311 if (strcmp(subclass
, ESC_DR_AP_STATE_CHANGE
) == 0) {
312 sysevent_process_dr(phys_path
, dev_name
);
325 sysevent_dev_add(gchar
*devfs_path
, gchar
*name
)
327 gchar
*parent_devfs_path
, *hotplug_devfs_path
;
330 HAL_INFO (("dev_add: %s %s", name
, devfs_path
));
332 parent
= hal_util_find_closest_ancestor (devfs_path
, &parent_devfs_path
, &hotplug_devfs_path
);
333 if (parent
== NULL
) {
337 HAL_INFO (("dev_add: parent=%s", parent_devfs_path
));
338 HAL_INFO (("dev_add: real=%s", hotplug_devfs_path
));
340 devinfo_add (parent
, hotplug_devfs_path
);
342 g_free (parent_devfs_path
);
343 g_free (hotplug_devfs_path
);
345 hotplug_event_process_queue ();
349 sysevent_dev_remove(gchar
*devfs_path
, gchar
*name
)
351 HAL_INFO (("dev_remove: %s %s", name
, devfs_path
));
353 devinfo_remove_branch (devfs_path
, NULL
);
354 hotplug_event_process_queue ();
358 sysevent_dev_branch(gchar
*devfs_path
)
360 HAL_INFO (("branch_remove: %s", devfs_path
));
362 devinfo_remove_branch (devfs_path
, NULL
);
363 hotplug_event_process_queue ();
367 sysevent_lofi_add(gchar
*devfs_path
, gchar
*name
)
370 const char *parent_udi
;
371 HalDevice
*d
, *parent
;
373 HAL_INFO (("lofi_add: %s %s", name
, devfs_path
));
375 if ((d
= hal_device_store_match_key_value_string (hald_get_gdl (),
376 "solaris.devfs_path", devfs_path
)) == NULL
) {
377 HAL_INFO (("device not found in GDL %s", devfs_path
));
380 parent_udi
= hal_device_property_get_string (d
, "info.parent");
381 if ((parent_udi
== NULL
) || (strlen(parent_udi
) == 0)) {
382 HAL_INFO (("parent not found in GDL %s", parent_udi
));
385 if ((parent
= hal_device_store_match_key_value_string (hald_get_gdl (),
386 "info.udi", parent_udi
)) == NULL
) {
387 HAL_INFO (("parent not found in GDL %s", parent_udi
));
391 if ((node
= di_init (devfs_path
, DINFOCPYALL
)) == DI_NODE_NIL
) {
392 HAL_INFO (("device not found in devinfo %s", devfs_path
));
396 HAL_INFO (("device %s parent %s", hal_device_get_udi (d
), parent_udi
));
397 devinfo_lofi_add_major (parent
, node
, devfs_path
, NULL
, TRUE
, d
);
401 hotplug_event_process_queue ();
405 sysevent_lofi_remove(gchar
*parent_devfs_path
, gchar
*name
)
407 devinfo_lofi_remove_minor(parent_devfs_path
, name
);
408 hotplug_event_process_queue ();
412 lookup_parent(char *devfs_path
)
415 HalDevice
*parent
= NULL
;
418 path
= strdup (devfs_path
);
419 p
= strrchr (path
, '/');
426 /* Look up the parent node in the gdl. */
427 parent
= hal_device_store_match_key_value_string (hald_get_gdl (),
428 "solaris.devfs_path", path
);
430 if (parent
== NULL
) {
431 /* Look up the parent node in the tdl. */
432 parent
= hal_device_store_match_key_value_string (hald_get_tdl (),
433 "solaris.devfs_path", path
);
441 * Handle the USB bus devices hot plugging events.
444 sysevent_devfs_add(gchar
*devfs_path
)
450 HAL_INFO (("devfs_handle: %s", devfs_path
));
452 if ((node
= di_init (devfs_path
, DINFOCPYALL
)) == DI_NODE_NIL
) {
453 HAL_INFO (("device not found in devinfo %s", devfs_path
));
457 if ((driver_name
= di_driver_name (node
)) == NULL
)
460 /* The disk and printer devices are handled by EC_DEV_ADD class. */
461 if ((strcmp (driver_name
, "scsa2usb") == 0) ||
462 (strcmp (driver_name
, "usbprn") == 0))
465 if ((parent
= lookup_parent (devfs_path
)) == NULL
)
468 devinfo_usb_add (parent
, node
, devfs_path
, NULL
);
472 hotplug_event_process_queue ();
481 sysevent_pwrctl(gchar
*class, gchar
*subclass
, gchar
*phys_path
,
482 gchar
*dev_name
, gchar
*dev_hid
, gchar
*dev_uid
, uint_t dev_index
)
484 const gchar prefix
[] = "/org/freedesktop/Hal/devices/pseudo/acpi_drv_0";
485 gchar udi
[HAL_PATH_MAX
];
487 if (strcmp(dev_hid
, "PNP0C0A") == 0) {
488 snprintf(udi
, sizeof(udi
), "%s_battery%d_0", prefix
, dev_index
);
489 devinfo_battery_rescan(phys_path
, udi
);
490 } else if (strcmp(dev_hid
, "ACPI0003") == 0) {
491 snprintf(udi
, sizeof (udi
), "%s_ac%d_0", prefix
, dev_index
);
492 devinfo_battery_rescan(phys_path
, udi
);
493 } else if (strcmp(dev_hid
, "PNP0C0D") == 0) {
494 snprintf(udi
, sizeof (udi
), "%s_lid_0", prefix
);
495 devinfo_lid_event(subclass
, udi
);
496 } else if (strcmp(subclass
, ESC_PWRCTL_POWER_BUTTON
) == 0) {
497 devinfo_power_button_event();
498 } else if ((strcmp(subclass
, ESC_PWRCTL_BRIGHTNESS_UP
) == 0) ||
499 (strcmp(subclass
, ESC_PWRCTL_BRIGHTNESS_DOWN
) == 0)) {
500 devinfo_brightness_hotkeys_event(subclass
);
502 HAL_INFO(("Unmatched EC_PWRCTL"));
507 sysevent_dr_remove_cpu()
512 HalDevice
*d
, *del_dev
;
513 int cpu_id
, del_cpuid
;
516 kstat_named_t
*ksdata
;
517 const char *cpu_devfs_path
;
519 * Find the CPU's that are DR removed. For each "processor" device in
520 * HAL device tree, check if it has its corresponding kstat_info. If
521 * not, then, that cpu has been removed and can remove the entry from
525 HAL_DEBUG (("sysevent_dr_remove_cpu()"));
528 HAL_INFO (("Error in removing HAL cpu entry during DR. Could"
529 " not open kstat to get cpu info: %s", strerror (errno
)));
534 * Iterate through the HAL device list to get the processor devices
536 gdl
= hald_get_gdl ();
539 while (iter
!= NULL
) {
540 d
= HAL_DEVICE (iter
->data
);
542 if (!hal_device_has_property (d
, "processor.number")) {
547 cpu_id
= hal_device_property_get_int (d
, "processor.number");
550 * Check if the above cpu_id has its info in kstat
553 ksp
= kstat_lookup (kc
, "cpu_info", cpu_id
, NULL
);
559 * kstat info not found. Delete the device entry
561 HAL_INFO ((" Remove CPU entry: %d", cpu_id
));
563 cpu_devfs_path
= hal_device_property_get_string (d
,
564 "solaris.devfs_path");
565 if (cpu_devfs_path
== NULL
) {
566 HAL_INFO (("Could not get cpu_devfs_path to "
567 "remove for cpu_id %d", cpu_id
));
570 * Remove the cpu device
572 HAL_DEBUG (("Queue %s for removal", cpu_devfs_path
));
573 devinfo_remove_enqueue ((char *)cpu_devfs_path
, NULL
);
574 hotplug_event_process_queue ();
584 sysevent_dr_insert_cpu(di_node_t node
, void *arg
)
587 char *device_type
= NULL
;
588 DevinfoDevHandler
*dh
;
590 dh
= &devinfo_cpu_handler
;
591 devfs_path
= di_devfs_path (node
);
593 (void) di_prop_lookup_strings (DDI_DEV_T_ANY
, node
, "device_type",
596 dh
->add (NULL
, node
, devfs_path
, device_type
);
598 di_devfs_path_free (devfs_path
);
599 return (DI_WALK_CONTINUE
);
603 * Remove/Add the DR event device
604 * Note: Currently it supports only CPU DR events
607 sysevent_process_dr(gchar
*ap_id
, gchar
*hint_val
)
610 cfga_list_data_t
*cfg_stat
;
615 if ((ap_id
== NULL
) || (hint_val
== NULL
))
617 HAL_DEBUG (("sysevent_process_dr: %s", ap_id
));
619 cfgerr
= config_list_ext (1, (char *const *)&ap_id
, &cfg_stat
, &nlist
,
620 NULL
, NULL
, &errstr
, 0);
622 if (cfgerr
!= CFGA_OK
) {
623 HAL_INFO (("DR sysevent process %d config_list_ext error: %s",
628 * Check if the device type is CPU
630 HAL_DEBUG ((" Ap-Type: %s, State: %d", cfg_stat
->ap_type
,
631 cfg_stat
->ap_r_state
));
632 if (strcmp (cfg_stat
->ap_type
, "CPU") == 0) {
633 if (strcmp (hint_val
, DR_HINT_REMOVE
) == 0) {
634 sysevent_dr_remove_cpu();
635 } else if (strcmp (hint_val
, DR_HINT_INSERT
) == 0) {
637 * Go through the device list and add the new cpu
641 di_init ("/", DINFOCPYALL
)) == DI_NODE_NIL
) {
642 HAL_INFO (("di_init failed. "\
643 "Cannot insert CPU"));
646 di_walk_node (root_node
, DI_WALK_CLDFIRST
, NULL
,
647 sysevent_dr_insert_cpu
);
649 hotplug_event_process_queue ();
652 HAL_INFO (("Not a CPU, so cannot DR"));