xref: /illumos-gate/usr/src/cmd/hal/utils/acpi.c (revision 22eb7cb54d8a6bcf6fe2674cb4b1f0cf2d85cfb6)
1 /***************************************************************************
2  *
3  * acpi.c : Main routines for setting battery, AC adapter, and lid properties
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 <unistd.h>
19 #include <strings.h>
20 #include <string.h>
21 #include <kstat.h>
22 #include <fcntl.h>
23 #include <errno.h>
24 #include <sys/acpi_drv.h>
25 
26 #include <libhal.h>
27 #include "../hald/device_info.h"
28 #include "../hald/hald_dbus.h"
29 #include "../hald/logger.h"
30 #include "../hald/util_pm.h"
31 #include "acpi.h"
32 
33 
34 static void
35 my_dbus_error_free(DBusError *error)
36 {
37 	if (dbus_error_is_set(error)) {
38 		dbus_error_free(error);
39 	}
40 }
41 
42 gboolean
43 laptop_panel_update(LibHalContext *ctx, const char *udi, int fd)
44 {
45 	LibHalChangeSet *cs;
46 	DBusError error;
47 	struct acpi_drv_output_info inf;
48 
49 	HAL_DEBUG(("laptop_panel_update() enter"));
50 
51 	dbus_error_init(&error);
52 	if (!libhal_device_query_capability(ctx, udi, "laptop_panel", &error)) {
53 		bzero(&inf, sizeof (inf));
54 		if ((ioctl(fd, ACPI_DRV_IOC_INFO, &inf) < 0) ||
55 		    (inf.nlev == 0)) {
56 			return (FALSE);
57 		}
58 
59 		my_dbus_error_free(&error);
60 		libhal_device_add_capability(ctx, udi, "laptop_panel", &error);
61 		if ((cs = libhal_device_new_changeset(udi)) == NULL) {
62 			my_dbus_error_free(&error);
63 			return (FALSE);
64 		}
65 		libhal_changeset_set_property_string(cs, "info.product",
66 		    "Generic Backlight Device");
67 		libhal_changeset_set_property_string(cs, "info.category",
68 		    "laptop_panel");
69 		libhal_changeset_set_property_int(cs, "laptop_panel.num_levels",
70 		    inf.nlev);
71 		my_dbus_error_free(&error);
72 		libhal_device_commit_changeset(ctx, cs, &error);
73 		libhal_device_free_changeset(cs);
74 	}
75 	my_dbus_error_free(&error);
76 	HAL_DEBUG(("ac_adapter_present() exit"));
77 	return (TRUE);
78 }
79 
80 gboolean
81 lid_update(LibHalContext *ctx, const char *udi, int fd)
82 {
83 	LibHalChangeSet *cs;
84 	DBusError error;
85 
86 	HAL_DEBUG(("lid_update() enter"));
87 
88 	dbus_error_init(&error);
89 	if (!libhal_device_query_capability(ctx, udi, "button", &error)) {
90 		my_dbus_error_free(&error);
91 		libhal_device_add_capability(ctx, udi, "button", &error);
92 		if ((cs = libhal_device_new_changeset(udi)) == NULL) {
93 			my_dbus_error_free(&error);
94 			return (FALSE);
95 		}
96 		libhal_changeset_set_property_bool(cs, "button.has_state",
97 		    TRUE);
98 		libhal_changeset_set_property_bool(cs, "button.state.value",
99 		    FALSE);
100 		libhal_changeset_set_property_string(cs, "button.type",
101 		    "lid");
102 		libhal_changeset_set_property_string(cs, "info.product",
103 		    "Lid Switch");
104 		libhal_changeset_set_property_string(cs, "info.category",
105 		    "button");
106 		my_dbus_error_free(&error);
107 		libhal_device_commit_changeset(ctx, cs, &error);
108 		libhal_device_free_changeset(cs);
109 	}
110 	my_dbus_error_free(&error);
111 	HAL_DEBUG(("update_lid() exit"));
112 	return (TRUE);
113 }
114 
115 static void
116 ac_adapter_present(LibHalContext *ctx, const char *udi, int fd)
117 {
118 	int pow;
119 	LibHalChangeSet *cs;
120 	DBusError error;
121 
122 	HAL_DEBUG(("ac_adapter_present() enter"));
123 	if (ioctl(fd, ACPI_DRV_IOC_POWER_STATUS, &pow) < 0) {
124 		return;
125 	}
126 	if ((cs = libhal_device_new_changeset(udi)) == NULL) {
127 		return;
128 	}
129 	if (pow > 0) {
130 		libhal_changeset_set_property_bool(cs, "ac_adapter.present",
131 		    TRUE);
132 	} else {
133 		libhal_changeset_set_property_bool(cs, "ac_adapter.present",
134 		    FALSE);
135 	}
136 
137 	dbus_error_init(&error);
138 	libhal_device_commit_changeset(ctx, cs, &error);
139 	libhal_device_free_changeset(cs);
140 	my_dbus_error_free(&error);
141 	HAL_DEBUG(("ac_adapter_present() exit"));
142 }
143 
144 static void
145 battery_remove(LibHalContext *ctx, const char *udi)
146 {
147 	DBusError error;
148 
149 	HAL_DEBUG(("battery_remove() enter"));
150 	dbus_error_init(&error);
151 	libhal_device_remove_property(ctx, udi, "battery.remaining_time",
152 	    &error);
153 	my_dbus_error_free(&error);
154 	libhal_device_remove_property(ctx, udi,
155 	    "battery.charge_level.percentage", &error);
156 	my_dbus_error_free(&error);
157 	libhal_device_remove_property(ctx, udi, "battery.charge_level.rate",
158 	    &error);
159 	my_dbus_error_free(&error);
160 	libhal_device_remove_property(ctx, udi,
161 	    "battery.charge_level.last_full", &error);
162 	my_dbus_error_free(&error);
163 	libhal_device_remove_property(ctx, udi,
164 	    "battery.charge_level.current", &error);
165 	my_dbus_error_free(&error);
166 	libhal_device_remove_property(ctx, udi, "battery.voltage.present",
167 	    &error);
168 	my_dbus_error_free(&error);
169 	libhal_device_remove_property(ctx, udi, "battery.reporting.rate",
170 	    &error);
171 	my_dbus_error_free(&error);
172 	libhal_device_remove_property(ctx, udi, "battery.reporting.current",
173 	    &error);
174 	my_dbus_error_free(&error);
175 	libhal_device_remove_property(ctx, udi,
176 	    "battery.rechargeable.is_discharging", &error);
177 	my_dbus_error_free(&error);
178 	libhal_device_remove_property(ctx, udi,
179 	    "battery.rechargeable.is_charging", &error);
180 	my_dbus_error_free(&error);
181 	libhal_device_remove_property(ctx, udi, "battery.is_rechargeable",
182 	    &error);
183 	my_dbus_error_free(&error);
184 	libhal_device_remove_property(ctx, udi, "battery.charge_level.unit",
185 	    &error);
186 	my_dbus_error_free(&error);
187 	libhal_device_remove_property(ctx, udi,
188 	    "battery.charge_level.granularity_2", &error);
189 	my_dbus_error_free(&error);
190 	libhal_device_remove_property(ctx, udi,
191 	    "battery.charge_level.granularity_1", &error);
192 	my_dbus_error_free(&error);
193 	libhal_device_remove_property(ctx, udi, "battery.charge_level.low",
194 	    &error);
195 	my_dbus_error_free(&error);
196 	libhal_device_remove_property(ctx, udi, "battery.charge_level.warning",
197 	    &error);
198 	my_dbus_error_free(&error);
199 	libhal_device_remove_property(ctx, udi, "battery.charge_level.design",
200 	    &error);
201 	my_dbus_error_free(&error);
202 	libhal_device_remove_property(ctx, udi, "battery.voltage.design",
203 	    &error);
204 	my_dbus_error_free(&error);
205 	libhal_device_remove_property(ctx, udi,
206 	    "battery.reporting.granularity_2", &error);
207 	my_dbus_error_free(&error);
208 	libhal_device_remove_property(ctx, udi,
209 	    "battery.reporting.granularity_1", &error);
210 	my_dbus_error_free(&error);
211 	libhal_device_remove_property(ctx, udi, "battery.reporting.low",
212 	    &error);
213 	my_dbus_error_free(&error);
214 	libhal_device_remove_property(ctx, udi, "battery.reporting.warning",
215 	    &error);
216 	my_dbus_error_free(&error);
217 	libhal_device_remove_property(ctx, udi, "battery.reporting.design",
218 	    &error);
219 	my_dbus_error_free(&error);
220 	libhal_device_remove_property(ctx, udi, "battery.reporting.last_full",
221 	    &error);
222 	my_dbus_error_free(&error);
223 	libhal_device_remove_property(ctx, udi, "battery.reporting.unit",
224 	    &error);
225 	my_dbus_error_free(&error);
226 	libhal_device_remove_property(ctx, udi, "battery.technology", &error);
227 	my_dbus_error_free(&error);
228 	libhal_device_remove_property(ctx, udi, "battery.reporting.technology",
229 	    &error);
230 	my_dbus_error_free(&error);
231 	libhal_device_remove_property(ctx, udi, "battery.serial", &error);
232 	my_dbus_error_free(&error);
233 	libhal_device_remove_property(ctx, udi, "battery.model", &error);
234 	my_dbus_error_free(&error);
235 	libhal_device_remove_property(ctx, udi, "battery.vendor", &error);
236 	my_dbus_error_free(&error);
237 	HAL_DEBUG(("battery_remove() exit"));
238 }
239 
240 static void
241 battery_last_full(LibHalChangeSet *cs, int fd)
242 {
243 	acpi_bif_t bif;
244 
245 	bzero(&bif, sizeof (bif));
246 	if (ioctl(fd, ACPI_DRV_IOC_INFO, &bif) < 0) {
247 		return;
248 	}
249 	libhal_changeset_set_property_int(cs, "battery.reporting_last_full",
250 	    bif.bif_last_cap);
251 }
252 
253 static void
254 battery_dynamic_update(LibHalContext *ctx, const char *udi, int fd)
255 {
256 	int reporting_rate;
257 	int reporting_current;
258 	int reporting_lastfull;
259 	int design_voltage;
260 	int present_voltage;
261 	char *reporting_unit;
262 	int remaining_time;
263 	int remaining_percentage;
264 	gboolean charging;
265 	gboolean discharging;
266 	acpi_bst_t bst;
267 	LibHalChangeSet *cs;
268 	DBusError error;
269 	static int counter = 0;
270 
271 	HAL_DEBUG(("battery_dynamic_update() enter"));
272 	bzero(&bst, sizeof (bst));
273 	if (ioctl(fd, ACPI_DRV_IOC_STATUS, &bst) < 0) {
274 		return;
275 	}
276 
277 	charging = bst.bst_state & ACPI_DRV_BST_CHARGING ? TRUE : FALSE;
278 	discharging = bst.bst_state & ACPI_DRV_BST_DISCHARGING ? TRUE : FALSE;
279 	/* No need to continue if battery is essentially idle. */
280 	if (counter && !charging && !discharging) {
281 		return;
282 	}
283 	dbus_error_init(&error);
284 	libhal_device_set_property_bool(ctx, udi, "battery.is_rechargeable",
285 	    TRUE, &error);
286 	my_dbus_error_free(&error);
287 	if (libhal_device_property_exists(ctx, udi,
288 	    "battery.charge_level.percentage", &error)) {
289 		remaining_percentage = libhal_device_get_property_int(ctx, udi,
290 		    "battery.charge_level.percentage", &error);
291 		if ((remaining_percentage == 100) && charging) {
292 			charging = FALSE;
293 		}
294 	}
295 	libhal_device_set_property_bool(ctx, udi,
296 	    "battery.rechargeable.is_charging", charging, &error);
297 	my_dbus_error_free(&error);
298 	libhal_device_set_property_bool(ctx, udi,
299 	    "battery.rechargeable.is_discharging", discharging, &error);
300 	my_dbus_error_free(&error);
301 	reporting_current = bst.bst_rem_cap;
302 	libhal_device_set_property_int(ctx, udi, "battery.reporting.current",
303 	    bst.bst_rem_cap, &error);
304 	my_dbus_error_free(&error);
305 	reporting_rate = bst.bst_rate;
306 	libhal_device_set_property_int(ctx, udi, "battery.reporting.rate",
307 	    bst.bst_rate, &error);
308 	my_dbus_error_free(&error);
309 	present_voltage = bst.bst_voltage;
310 	libhal_device_set_property_int(ctx, udi, "battery.voltage.present",
311 	    bst.bst_voltage, &error);
312 	/* get all the data we know */
313 	my_dbus_error_free(&error);
314 	reporting_unit = libhal_device_get_property_string(ctx, udi,
315 	    "battery.reporting.unit", &error);
316 	my_dbus_error_free(&error);
317 	reporting_lastfull = libhal_device_get_property_int(ctx, udi,
318 	    "battery.reporting.last_full", &error);
319 
320 	/*
321 	 * Convert mAh to mWh since util_compute_time_remaining() works
322 	 * for mWh.
323 	 */
324 	if (reporting_unit && strcmp(reporting_unit, "mAh") == 0) {
325 		my_dbus_error_free(&error);
326 		design_voltage = libhal_device_get_property_int(ctx, udi,
327 		    "battery.voltage.design", &error);
328 		/*
329 		 * If the present_voltage is inaccurate, set it to the
330 		 * design_voltage.
331 		 */
332 		if (((present_voltage * 10) < design_voltage) ||
333 		    (present_voltage <= 0) ||
334 		    (present_voltage > design_voltage)) {
335 			present_voltage = design_voltage;
336 		}
337 		reporting_rate = (reporting_rate * present_voltage) / 1000;
338 		reporting_lastfull = (reporting_lastfull * present_voltage) /
339 		    1000;
340 		reporting_current = (reporting_current * present_voltage) /
341 		    1000;
342 	}
343 
344 	/* Make sure the current charge does not exceed the full charge */
345 	if (reporting_current > reporting_lastfull) {
346 		reporting_current = reporting_lastfull;
347 	}
348 	if (!charging && !discharging) {
349 		counter++;
350 		reporting_rate = 0;
351 	}
352 
353 	if ((cs = libhal_device_new_changeset(udi)) == NULL) {
354 		HAL_DEBUG(("Cannot allocate changeset"));
355 		libhal_free_string(reporting_unit);
356 		my_dbus_error_free(&error);
357 		return;
358 	}
359 
360 	libhal_changeset_set_property_int(cs, "battery.charge_level.rate",
361 	    reporting_rate);
362 	libhal_changeset_set_property_int(cs,
363 	    "battery.charge_level.last_full", reporting_lastfull);
364 	libhal_changeset_set_property_int(cs,
365 	    "battery.charge_level.current", reporting_current);
366 
367 	remaining_percentage = util_compute_percentage_charge(udi,
368 	    reporting_current, reporting_lastfull);
369 	remaining_time = util_compute_time_remaining(udi, reporting_rate,
370 	    reporting_current, reporting_lastfull, discharging, charging, 0);
371 	/*
372 	 * Some batteries give bad remaining_time estimates relative to
373 	 * the charge level.
374 	 */
375 	if (charging && ((remaining_time < 30) || ((remaining_time < 300) &&
376 	    (remaining_percentage < 95)) || (remaining_percentage > 97))) {
377 		remaining_time = util_compute_time_remaining(udi,
378 		    reporting_rate, reporting_current, reporting_lastfull,
379 		    discharging, charging, 1);
380 	}
381 
382 	if (remaining_percentage > 0) {
383 		libhal_changeset_set_property_int(cs,
384 		    "battery.charge_level.percentage", remaining_percentage);
385 	} else {
386 		my_dbus_error_free(&error);
387 		libhal_device_remove_property(ctx, udi,
388 		    "battery.charge_level.percentage", &error);
389 	}
390 	if ((remaining_percentage == 100) && charging) {
391 		battery_last_full(cs, fd);
392 	}
393 	/*
394 	 * remaining_percentage is more accurate so we handle cases
395 	 * where the remaining_time cannot be correct.
396 	 */
397 	if ((!charging && !discharging) || ((remaining_percentage == 100) &&
398 	    !discharging)) {
399 		remaining_time = 0;
400 	}
401 	if (remaining_time < 0) {
402 		my_dbus_error_free(&error);
403 		libhal_device_remove_property(ctx, udi,
404 		    "battery.remaining_time", &error);
405 	} else if (remaining_time >= 0) {
406 		libhal_changeset_set_property_int(cs,
407 		    "battery.remaining_time", remaining_time);
408 	}
409 
410 	my_dbus_error_free(&error);
411 	libhal_device_commit_changeset(ctx, cs, &error);
412 	libhal_device_free_changeset(cs);
413 	libhal_free_string(reporting_unit);
414 	my_dbus_error_free(&error);
415 	HAL_DEBUG(("battery_dynamic_update() exit"));
416 }
417 
418 static gboolean
419 battery_static_update(LibHalContext *ctx, const char *udi, int fd)
420 {
421 	const char *technology;
422 	int reporting_design;
423 	int reporting_warning;
424 	int reporting_low;
425 	int reporting_gran1;
426 	int reporting_gran2;
427 	int voltage_design;
428 	char reporting_unit[10];
429 	acpi_bif_t bif;
430 	LibHalChangeSet *cs;
431 	DBusError error;
432 
433 	HAL_DEBUG(("battery_static_update() enter"));
434 	bzero(&bif, sizeof (bif));
435 	if (ioctl(fd, ACPI_DRV_IOC_INFO, &bif) < 0) {
436 		return (FALSE);
437 	}
438 	if ((cs = libhal_device_new_changeset(udi)) == NULL) {
439 		HAL_DEBUG(("Cannot allocate changeset"));
440 		return (FALSE);
441 	}
442 
443 	libhal_changeset_set_property_string(cs, "battery.vendor",
444 	    bif.bif_oem_info);
445 	technology = bif.bif_type;
446 	if (technology != NULL) {
447 		libhal_changeset_set_property_string(cs,
448 		    "battery.reporting.technology", technology);
449 		libhal_changeset_set_property_string(cs, "battery.technology",
450 		    util_get_battery_technology(technology));
451 	}
452 	libhal_changeset_set_property_string(cs, "battery.serial",
453 	    bif.bif_serial);
454 	libhal_changeset_set_property_string(cs, "battery.model",
455 	    bif.bif_model);
456 
457 	if (bif.bif_unit) {
458 		libhal_changeset_set_property_string(cs,
459 		    "battery.reporting.unit", "mAh");
460 		strlcpy(reporting_unit, "mAh", sizeof (reporting_unit));
461 	} else {
462 		libhal_changeset_set_property_string(cs,
463 		    "battery.reporting.unit", "mWh");
464 		strlcpy(reporting_unit, "mWh", sizeof (reporting_unit));
465 	}
466 	libhal_changeset_set_property_int(cs, "battery.reporting.last_full",
467 	    bif.bif_last_cap);
468 	libhal_changeset_set_property_int(cs, "battery.reporting.design",
469 	    bif.bif_design_cap);
470 	reporting_design = bif.bif_design_cap;
471 	libhal_changeset_set_property_int(cs, "battery.reporting.warning",
472 	    bif.bif_warn_cap);
473 	reporting_warning = bif.bif_warn_cap;
474 	libhal_changeset_set_property_int(cs, "battery.reporting.low",
475 	    bif.bif_low_cap);
476 	reporting_low = bif.bif_low_cap;
477 	libhal_changeset_set_property_int(cs,
478 	    "battery.reporting.granularity_1", bif.bif_gran1_cap);
479 	reporting_gran1 = bif.bif_gran1_cap;
480 	libhal_changeset_set_property_int(cs,
481 	    "battery.reporting.granularity_2", bif.bif_gran2_cap);
482 	reporting_gran2 = bif.bif_gran2_cap;
483 	libhal_changeset_set_property_int(cs, "battery.voltage.design",
484 	    bif.bif_voltage);
485 	voltage_design = bif.bif_voltage;
486 
487 	if (reporting_unit && strcmp(reporting_unit, "mAh") == 0) {
488 		/* convert to mWh */
489 		libhal_changeset_set_property_string(cs,
490 		    "battery.charge_level.unit", "mWh");
491 		libhal_changeset_set_property_int(cs,
492 		    "battery.charge_level.design",
493 		    (reporting_design * voltage_design) / 1000);
494 		libhal_changeset_set_property_int(cs,
495 		    "battery.charge_level.warning",
496 		    (reporting_warning * voltage_design) / 1000);
497 		libhal_changeset_set_property_int(cs,
498 		    "battery.charge_level.low",
499 		    (reporting_low * voltage_design) / 1000);
500 		libhal_changeset_set_property_int(cs,
501 		    "battery.charge_level.granularity_1",
502 		    (reporting_gran1 * voltage_design) / 1000);
503 		libhal_changeset_set_property_int(cs,
504 		    "battery.charge_level.granularity_2",
505 		    (reporting_gran2 * voltage_design) / 1000);
506 	} else {
507 		if (reporting_unit && strcmp(reporting_unit, "mWh") == 0) {
508 			libhal_changeset_set_property_string(cs,
509 			    "battery.charge_level.unit", "mWh");
510 		}
511 		libhal_changeset_set_property_int(cs,
512 		    "battery.charge_level.design", reporting_design);
513 		libhal_changeset_set_property_int(cs,
514 		    "battery.charge_level.warning", reporting_warning);
515 		libhal_changeset_set_property_int(cs,
516 		    "battery.charge_level.low", reporting_low);
517 		libhal_changeset_set_property_int(cs,
518 		    "battery.charge_level.granularity_1", reporting_gran1);
519 		libhal_changeset_set_property_int(cs,
520 		    "battery.charge_level.granularity_2", reporting_gran2);
521 	}
522 
523 
524 	dbus_error_init(&error);
525 	libhal_device_commit_changeset(ctx, cs, &error);
526 	libhal_device_free_changeset(cs);
527 	my_dbus_error_free(&error);
528 	HAL_DEBUG(("battery_static_update() exit"));
529 	return (TRUE);
530 }
531 
532 gboolean
533 battery_update(LibHalContext *ctx, const char *udi, int fd)
534 {
535 	acpi_bst_t bst;
536 	DBusError error;
537 
538 	HAL_DEBUG(("battery_update() enter"));
539 	dbus_error_init(&error);
540 	libhal_device_set_property_string(ctx, udi, "info.product",
541 	    "Battery Bay", &error);
542 	my_dbus_error_free(&error);
543 	libhal_device_set_property_string(ctx, udi, "info.category", "battery",
544 	    &error);
545 
546 	bzero(&bst, sizeof (bst));
547 	if (ioctl(fd, ACPI_DRV_IOC_STATUS, &bst) < 0) {
548 		if (errno == ENXIO) {
549 			my_dbus_error_free(&error);
550 			libhal_device_set_property_bool(ctx, udi,
551 			    "battery.present", FALSE, &error);
552 		} else {
553 			my_dbus_error_free(&error);
554 			return (FALSE);
555 		}
556 	} else {
557 		my_dbus_error_free(&error);
558 		libhal_device_set_property_bool(ctx, udi, "battery.present",
559 		    TRUE, &error);
560 	}
561 
562 	my_dbus_error_free(&error);
563 	if (!libhal_device_get_property_bool(ctx, udi, "battery.present",
564 	    &error)) {
565 		HAL_DEBUG(("battery_update(): battery is NOT present"));
566 		battery_remove(ctx, udi);
567 	} else {
568 		HAL_DEBUG(("battery_update(): battery is present"));
569 		my_dbus_error_free(&error);
570 		libhal_device_set_property_string(ctx, udi, "battery.type",
571 		    "primary", &error);
572 		my_dbus_error_free(&error);
573 		libhal_device_add_capability(ctx, udi, "battery", &error);
574 		my_dbus_error_free(&error);
575 		if (libhal_device_get_property_type(ctx, udi, "battery.vendor",
576 		    &error) == LIBHAL_PROPERTY_TYPE_INVALID) {
577 			battery_static_update(ctx, udi, fd);
578 		}
579 		battery_dynamic_update(ctx, udi, fd);
580 	}
581 	my_dbus_error_free(&error);
582 	HAL_DEBUG(("battery_update() exit"));
583 	return (TRUE);
584 }
585 
586 static gboolean
587 battery_update_all(LibHalContext *ctx)
588 {
589 	int i;
590 	int num_devices;
591 	char **battery_devices;
592 	int fd;
593 	DBusError error;
594 
595 	HAL_DEBUG(("battery_update_all() enter"));
596 
597 	dbus_error_init(&error);
598 	if ((battery_devices = libhal_manager_find_device_string_match
599 	    (ctx, "info.category", "battery", &num_devices, &error)) !=
600 	    NULL) {
601 		for (i = 0; i < num_devices; i++) {
602 			my_dbus_error_free(&error);
603 			if (libhal_device_get_property_bool(ctx,
604 			    battery_devices[i], "battery.present", &error)) {
605 				if ((fd = open_device(ctx,
606 				    battery_devices[i])) == -1) {
607 					continue;
608 				}
609 				battery_dynamic_update(ctx, battery_devices[i],
610 				    fd);
611 				close(fd);
612 			}
613 		}
614 		libhal_free_string_array(battery_devices);
615 	}
616 	my_dbus_error_free(&error);
617 	HAL_DEBUG(("battery_update_all() exit"));
618 	return (TRUE);
619 }
620 
621 gboolean
622 ac_adapter_update(LibHalContext *ctx, const char *udi, int fd)
623 {
624 	LibHalChangeSet *cs;
625 	DBusError error;
626 
627 	HAL_DEBUG(("ac_adapter_update() enter"));
628 	dbus_error_init(&error);
629 	if (!libhal_device_query_capability(ctx, udi, "ac_adapter", &error)) {
630 		my_dbus_error_free(&error);
631 		libhal_device_add_capability(ctx, udi, "ac_adapter", &error);
632 		if ((cs = libhal_device_new_changeset(udi)) == NULL) {
633 			my_dbus_error_free(&error);
634 			return (FALSE);
635 		}
636 		libhal_changeset_set_property_string(cs, "info.product",
637 		    "AC Adapter");
638 		libhal_changeset_set_property_string(cs, "info.category",
639 		    "ac_adapter");
640 		my_dbus_error_free(&error);
641 		libhal_device_commit_changeset(ctx, cs, &error);
642 		libhal_device_free_changeset(cs);
643 	}
644 	ac_adapter_present(ctx, udi, fd);
645 	battery_update_all(ctx);
646 
647 	my_dbus_error_free(&error);
648 	HAL_DEBUG(("ac_adapter_update() exit"));
649 	return (TRUE);
650 }
651 
652 static gboolean
653 ac_adapter_update_all(LibHalContext *ctx)
654 {
655 	int i;
656 	int num_devices;
657 	char **ac_adapter_devices;
658 	int fd;
659 	DBusError error;
660 
661 	HAL_DEBUG(("ac_adapter_update_all() enter"));
662 	dbus_error_init(&error);
663 	if ((ac_adapter_devices = libhal_manager_find_device_string_match(
664 	    ctx, "info.category", "ac_adapter", &num_devices, &error)) !=
665 	    NULL) {
666 		for (i = 0; i < num_devices; i++) {
667 			if ((fd = open_device(ctx, ac_adapter_devices[i]))
668 			    == -1) {
669 				continue;
670 			}
671 			ac_adapter_present(ctx, ac_adapter_devices[i], fd);
672 			close(fd);
673 		}
674 		libhal_free_string_array(ac_adapter_devices);
675 	}
676 	my_dbus_error_free(&error);
677 	HAL_DEBUG(("ac_adapter_update_all() exit"));
678 	return (TRUE);
679 }
680 
681 gboolean
682 update_devices(gpointer data)
683 {
684 	LibHalContext *ctx = (LibHalContext *)data;
685 
686 	HAL_DEBUG(("update_devices() enter"));
687 	ac_adapter_update_all(ctx);
688 	battery_update_all(ctx);
689 	HAL_DEBUG(("update_devices() exit"));
690 	return (TRUE);
691 }
692 
693 int
694 open_device(LibHalContext *ctx, char *udi)
695 {
696 	char path[HAL_PATH_MAX] = "/devices";
697 	char *devfs_path;
698 	DBusError error;
699 
700 	dbus_error_init(&error);
701 	devfs_path = libhal_device_get_property_string(ctx, udi,
702 	    "solaris.devfs_path", &error);
703 	my_dbus_error_free(&error);
704 	if (devfs_path == NULL) {
705 		return (-1);
706 	}
707 	strlcat(path, devfs_path, HAL_PATH_MAX);
708 	libhal_free_string(devfs_path);
709 	return (open(path, O_RDONLY | O_NONBLOCK));
710 }
711