1 /***************************************************************************
3 * acpi.c : Main routines for setting battery, AC adapter, and lid properties
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 **************************************************************************/
22 #include <sys/acpi_drv.h>
25 #include "../hald/device_info.h"
26 #include "../hald/hald_dbus.h"
27 #include "../hald/logger.h"
28 #include "../hald/util_pm.h"
33 my_dbus_error_free(DBusError
*error
)
35 if (dbus_error_is_set(error
)) {
36 dbus_error_free(error
);
41 laptop_panel_update(LibHalContext
*ctx
, const char *udi
, int fd
)
45 struct acpi_drv_output_info inf
;
47 HAL_DEBUG(("laptop_panel_update() enter"));
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) ||
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
);
63 libhal_changeset_set_property_string(cs
, "info.product",
64 "Generic Backlight Device");
65 libhal_changeset_set_property_string(cs
, "info.category",
67 libhal_changeset_set_property_int(cs
, "laptop_panel.num_levels",
69 my_dbus_error_free(&error
);
70 libhal_device_commit_changeset(ctx
, cs
, &error
);
71 libhal_device_free_changeset(cs
);
73 my_dbus_error_free(&error
);
74 HAL_DEBUG(("ac_adapter_present() exit"));
79 lid_update(LibHalContext
*ctx
, const char *udi
, int fd
)
85 HAL_DEBUG(("lid_update() enter"));
87 if ((cs
= libhal_device_new_changeset(udi
)) == NULL
) {
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",
98 if (ioctl(fd
, ACPI_DRV_IOC_LID_STATUS
, &lid_state
) < 0) {
101 if (lid_state
!= 0) {
103 libhal_changeset_set_property_bool(cs
,
104 "button.state.value", FALSE
);
107 libhal_changeset_set_property_bool(cs
,
108 "button.state.value", TRUE
);
110 libhal_changeset_set_property_bool(cs
, "button.workaround",
112 libhal_changeset_set_property_string(cs
, "button.type",
114 libhal_changeset_set_property_string(cs
, "info.product",
116 libhal_changeset_set_property_string(cs
, "info.category",
119 my_dbus_error_free(&error
);
120 if (ioctl(fd
, ACPI_DRV_IOC_LID_UPDATE
, &lid_state
) < 0) {
123 if (lid_state
!= 0) {
125 libhal_changeset_set_property_bool(cs
,
126 "button.state.value", FALSE
);
129 libhal_changeset_set_property_bool(cs
,
130 "button.state.value", TRUE
);
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"));
142 ac_adapter_present(LibHalContext
*ctx
, const char *udi
, int fd
)
148 HAL_DEBUG(("ac_adapter_present() enter"));
149 if (ioctl(fd
, ACPI_DRV_IOC_POWER_STATUS
, &pow
) < 0) {
152 if ((cs
= libhal_device_new_changeset(udi
)) == NULL
) {
156 libhal_changeset_set_property_bool(cs
, "ac_adapter.present",
159 libhal_changeset_set_property_bool(cs
, "ac_adapter.present",
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"));
171 battery_remove(LibHalContext
*ctx
, const char *udi
)
175 HAL_DEBUG(("battery_remove() enter"));
176 dbus_error_init(&error
);
177 libhal_device_remove_property(ctx
, udi
, "battery.remaining_time",
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",
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",
194 my_dbus_error_free(&error
);
195 libhal_device_remove_property(ctx
, udi
, "battery.reporting.rate",
197 my_dbus_error_free(&error
);
198 libhal_device_remove_property(ctx
, udi
, "battery.reporting.current",
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",
209 my_dbus_error_free(&error
);
210 libhal_device_remove_property(ctx
, udi
, "battery.charge_level.unit",
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",
221 my_dbus_error_free(&error
);
222 libhal_device_remove_property(ctx
, udi
, "battery.charge_level.warning",
224 my_dbus_error_free(&error
);
225 libhal_device_remove_property(ctx
, udi
, "battery.charge_level.design",
227 my_dbus_error_free(&error
);
228 libhal_device_remove_property(ctx
, udi
, "battery.voltage.design",
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",
239 my_dbus_error_free(&error
);
240 libhal_device_remove_property(ctx
, udi
, "battery.reporting.warning",
242 my_dbus_error_free(&error
);
243 libhal_device_remove_property(ctx
, udi
, "battery.reporting.design",
245 my_dbus_error_free(&error
);
246 libhal_device_remove_property(ctx
, udi
, "battery.reporting.last_full",
248 my_dbus_error_free(&error
);
249 libhal_device_remove_property(ctx
, udi
, "battery.reporting.unit",
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",
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"));
267 battery_last_full(LibHalChangeSet
*cs
, int fd
)
271 bzero(&bif
, sizeof (bif
));
272 if (ioctl(fd
, ACPI_DRV_IOC_INFO
, &bif
) < 0) {
275 libhal_changeset_set_property_int(cs
, "battery.reporting_last_full",
280 battery_dynamic_update(LibHalContext
*ctx
, const char *udi
, int fd
)
283 int reporting_current
;
284 int reporting_lastfull
;
287 char *reporting_unit
;
289 int remaining_percentage
;
291 gboolean discharging
;
295 static int counter
= 0;
297 HAL_DEBUG(("battery_dynamic_update() enter"));
298 bzero(&bst
, sizeof (bst
));
299 if (ioctl(fd
, ACPI_DRV_IOC_STATUS
, &bst
) < 0) {
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
) {
309 dbus_error_init(&error
);
310 libhal_device_set_property_bool(ctx
, udi
, "battery.is_rechargeable",
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
) {
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
);
347 * Convert mAh to mWh since util_compute_time_remaining() works
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
);
355 * If the present_voltage is inaccurate, set it to the
358 if (((present_voltage
* 10) < design_voltage
) ||
359 (present_voltage
<= 0) ||
360 (present_voltage
> design_voltage
)) {
361 present_voltage
= design_voltage
;
363 reporting_rate
= (reporting_rate
* present_voltage
) / 1000;
364 reporting_lastfull
= (reporting_lastfull
* present_voltage
) /
366 reporting_current
= (reporting_current
* present_voltage
) /
370 /* Make sure the current charge does not exceed the full charge */
371 if (reporting_current
> reporting_lastfull
) {
372 reporting_current
= reporting_lastfull
;
374 if (!charging
&& !discharging
) {
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
);
386 libhal_changeset_set_property_int(cs
, "battery.charge_level.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
);
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);
398 * Some batteries give bad remaining_time estimates relative to
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);
408 if (remaining_percentage
> 0) {
409 libhal_changeset_set_property_int(cs
,
410 "battery.charge_level.percentage", remaining_percentage
);
412 my_dbus_error_free(&error
);
413 libhal_device_remove_property(ctx
, udi
,
414 "battery.charge_level.percentage", &error
);
416 if ((remaining_percentage
== 100) && charging
) {
417 battery_last_full(cs
, fd
);
420 * remaining_percentage is more accurate so we handle cases
421 * where the remaining_time cannot be correct.
423 if ((!charging
&& !discharging
) || ((remaining_percentage
== 100) &&
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
);
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"));
445 battery_static_update(LibHalContext
*ctx
, const char *udi
, int fd
)
447 const char *technology
;
448 int reporting_design
;
449 int reporting_warning
;
454 char reporting_unit
[10];
459 HAL_DEBUG(("battery_static_update() enter"));
460 bzero(&bif
, sizeof (bif
));
461 if (ioctl(fd
, ACPI_DRV_IOC_INFO
, &bif
) < 0) {
464 if ((cs
= libhal_device_new_changeset(udi
)) == NULL
) {
465 HAL_DEBUG(("Cannot allocate changeset"));
469 libhal_changeset_set_property_string(cs
, "battery.vendor",
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
));
478 libhal_changeset_set_property_string(cs
, "battery.serial",
480 libhal_changeset_set_property_string(cs
, "battery.model",
484 libhal_changeset_set_property_string(cs
,
485 "battery.reporting.unit", "mAh");
486 strlcpy(reporting_unit
, "mAh", sizeof (reporting_unit
));
488 libhal_changeset_set_property_string(cs
,
489 "battery.reporting.unit", "mWh");
490 strlcpy(reporting_unit
, "mWh", sizeof (reporting_unit
));
492 libhal_changeset_set_property_int(cs
, "battery.reporting.last_full",
494 libhal_changeset_set_property_int(cs
, "battery.reporting.design",
496 reporting_design
= bif
.bif_design_cap
;
497 libhal_changeset_set_property_int(cs
, "battery.reporting.warning",
499 reporting_warning
= bif
.bif_warn_cap
;
500 libhal_changeset_set_property_int(cs
, "battery.reporting.low",
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",
511 voltage_design
= bif
.bif_voltage
;
513 if (reporting_unit
&& strcmp(reporting_unit
, "mAh") == 0) {
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);
533 if (reporting_unit
&& strcmp(reporting_unit
, "mWh") == 0) {
534 libhal_changeset_set_property_string(cs
,
535 "battery.charge_level.unit", "mWh");
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
);
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"));
559 battery_update(LibHalContext
*ctx
, const char *udi
, int fd
)
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",
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
);
579 my_dbus_error_free(&error
);
583 my_dbus_error_free(&error
);
584 libhal_device_set_property_bool(ctx
, udi
, "battery.present",
588 my_dbus_error_free(&error
);
589 if (!libhal_device_get_property_bool(ctx
, udi
, "battery.present",
591 HAL_DEBUG(("battery_update(): battery is NOT present"));
592 battery_remove(ctx
, udi
);
594 HAL_DEBUG(("battery_update(): battery is present"));
595 my_dbus_error_free(&error
);
596 libhal_device_set_property_string(ctx
, udi
, "battery.type",
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
);
605 battery_dynamic_update(ctx
, udi
, fd
);
607 my_dbus_error_free(&error
);
608 HAL_DEBUG(("battery_update() exit"));
613 battery_update_all(LibHalContext
*ctx
)
617 char **battery_devices
;
621 HAL_DEBUG(("battery_update_all() enter"));
623 dbus_error_init(&error
);
624 if ((battery_devices
= libhal_manager_find_device_string_match
625 (ctx
, "info.category", "battery", &num_devices
, &error
)) !=
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) {
635 battery_dynamic_update(ctx
, battery_devices
[i
],
640 libhal_free_string_array(battery_devices
);
642 my_dbus_error_free(&error
);
643 HAL_DEBUG(("battery_update_all() exit"));
648 ac_adapter_update(LibHalContext
*ctx
, const char *udi
, int fd
)
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
);
662 libhal_changeset_set_property_string(cs
, "info.product",
664 libhal_changeset_set_property_string(cs
, "info.category",
666 my_dbus_error_free(&error
);
667 libhal_device_commit_changeset(ctx
, cs
, &error
);
668 libhal_device_free_changeset(cs
);
670 ac_adapter_present(ctx
, udi
, fd
);
671 battery_update_all(ctx
);
673 my_dbus_error_free(&error
);
674 HAL_DEBUG(("ac_adapter_update() exit"));
679 ac_adapter_update_all(LibHalContext
*ctx
)
683 char **ac_adapter_devices
;
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
)) !=
692 for (i
= 0; i
< num_devices
; i
++) {
693 if ((fd
= open_device(ctx
, ac_adapter_devices
[i
]))
697 ac_adapter_present(ctx
, ac_adapter_devices
[i
], fd
);
700 libhal_free_string_array(ac_adapter_devices
);
702 my_dbus_error_free(&error
);
703 HAL_DEBUG(("ac_adapter_update_all() exit"));
708 update_devices(gpointer data
)
710 LibHalContext
*ctx
= (LibHalContext
*)data
;
712 HAL_DEBUG(("update_devices() enter"));
713 ac_adapter_update_all(ctx
);
714 battery_update_all(ctx
);
715 HAL_DEBUG(("update_devices() exit"));
720 open_device(LibHalContext
*ctx
, char *udi
)
722 char path
[HAL_PATH_MAX
] = "/devices";
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
) {
733 strlcat(path
, devfs_path
, HAL_PATH_MAX
);
734 libhal_free_string(devfs_path
);
735 return (open(path
, O_RDONLY
| O_NONBLOCK
));