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