acpibat.c revision 1.27
1/* $OpenBSD: acpibat.c,v 1.27 2006/10/19 08:56:46 marco Exp $ */ 2/* 3 * Copyright (c) 2005 Marco Peereboom <marco@openbsd.org> 4 * 5 * Permission to use, copy, modify, and distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 */ 17 18#include <sys/param.h> 19#include <sys/proc.h> 20#include <sys/systm.h> 21#include <sys/device.h> 22#include <sys/rwlock.h> 23#include <sys/malloc.h> 24#include <sys/sensors.h> 25 26#include <machine/bus.h> 27 28#include <dev/acpi/acpireg.h> 29#include <dev/acpi/acpivar.h> 30#include <dev/acpi/acpidev.h> 31#include <dev/acpi/amltypes.h> 32#include <dev/acpi/dsdt.h> 33 34int acpibat_match(struct device *, void *, void *); 35void acpibat_attach(struct device *, struct device *, void *); 36 37struct cfattach acpibat_ca = { 38 sizeof(struct acpibat_softc), acpibat_match, acpibat_attach 39}; 40 41struct cfdriver acpibat_cd = { 42 NULL, "acpibat", DV_DULL 43}; 44 45void acpibat_monitor(struct acpibat_softc *); 46void acpibat_refresh(void *); 47int acpibat_getbif(struct acpibat_softc *); 48int acpibat_getbst(struct acpibat_softc *); 49int acpibat_notify(struct aml_node *, int, void *); 50 51int 52acpibat_match(struct device *parent, void *match, void *aux) 53{ 54 struct acpi_attach_args *aa = aux; 55 struct cfdata *cf = match; 56 57 /* sanity */ 58 if (aa->aaa_name == NULL || 59 strcmp(aa->aaa_name, cf->cf_driver->cd_name) != 0 || 60 aa->aaa_table != NULL) 61 return (0); 62 63 return (1); 64} 65 66void 67acpibat_attach(struct device *parent, struct device *self, void *aux) 68{ 69 struct acpibat_softc *sc = (struct acpibat_softc *)self; 70 struct acpi_attach_args *aa = aux; 71 struct aml_value res; 72 73 sc->sc_acpi = (struct acpi_softc *)parent; 74 sc->sc_devnode = aa->aaa_node->child; 75 76 rw_init(&sc->sc_lock, "acpibat"); 77 78 /* XXX this trick seems to only work during boot */ 79 if (aml_evalname(sc->sc_acpi, sc->sc_devnode, "_STA", 0, NULL, &res) != 0) 80 dnprintf(10, "%s: no _STA\n", 81 DEVNAME(sc)); 82 83 if (!(res.v_integer & STA_BATTERY)) { 84 sc->sc_bat_present = 0; 85 printf(": %s: not present\n", sc->sc_devnode->parent->name); 86 acpibat_monitor(sc); 87 } else { 88 sc->sc_bat_present = 1; 89 90 acpibat_getbif(sc); 91 acpibat_getbst(sc); 92 93 printf(": %s: model: %s serial: %s type: %s oem: %s\n", 94 sc->sc_devnode->parent->name, 95 sc->sc_bif.bif_model, 96 sc->sc_bif.bif_serial, 97 sc->sc_bif.bif_type, 98 sc->sc_bif.bif_oem); 99 100 if (sensor_task_register(sc, acpibat_refresh, 10)) 101 printf(", unable to register update task\n"); 102 103 acpibat_monitor(sc); 104 105 } 106 aml_freevalue(&res); 107 108 aml_register_notify(sc->sc_devnode->parent, aa->aaa_dev, 109 acpibat_notify, sc); 110} 111 112/* XXX this is for debug only, remove later */ 113void 114acpibat_monitor(struct acpibat_softc *sc) 115{ 116 int i, type; 117 118 /* assume _BIF and _BST have been called */ 119 120 memset(sc->sc_sens, 0, sizeof(sc->sc_sens)); 121 for (i = 0; i < 8; i++) 122 strlcpy(sc->sc_sens[i].device, DEVNAME(sc), 123 sizeof(sc->sc_sens[i].device)); 124 125 type = sc->sc_bif.bif_power_unit ? SENSOR_AMPHOUR : SENSOR_WATTHOUR; 126 127 strlcpy(sc->sc_sens[0].desc, "last full capacity", 128 sizeof(sc->sc_sens[0].desc)); 129 sc->sc_sens[0].type = type; 130 sensor_add(&sc->sc_sens[0]); 131 sc->sc_sens[0].value = sc->sc_bif.bif_last_capacity * 1000; 132 133 strlcpy(sc->sc_sens[1].desc, "warning capacity", 134 sizeof(sc->sc_sens[1].desc)); 135 sc->sc_sens[1].type = type; 136 sensor_add(&sc->sc_sens[1]); 137 sc->sc_sens[1].value = sc->sc_bif.bif_warning * 1000; 138 139 strlcpy(sc->sc_sens[2].desc, "low capacity", 140 sizeof(sc->sc_sens[2].desc)); 141 sc->sc_sens[2].type = type; 142 sensor_add(&sc->sc_sens[2]); 143 sc->sc_sens[2].value = sc->sc_bif.bif_low * 1000; 144 145 strlcpy(sc->sc_sens[3].desc, "voltage", sizeof(sc->sc_sens[3].desc)); 146 sc->sc_sens[3].type = SENSOR_VOLTS_DC; 147 sensor_add(&sc->sc_sens[3]); 148 sc->sc_sens[3].status = SENSOR_S_OK; 149 sc->sc_sens[3].value = sc->sc_bif.bif_voltage * 1000; 150 151 strlcpy(sc->sc_sens[4].desc, "state", sizeof(sc->sc_sens[4].desc)); 152 sc->sc_sens[4].type = SENSOR_INTEGER; 153 sensor_add(&sc->sc_sens[4]); 154 sc->sc_sens[4].status = SENSOR_S_OK; 155 sc->sc_sens[4].value = sc->sc_bst.bst_state; 156 157 strlcpy(sc->sc_sens[5].desc, "rate", sizeof(sc->sc_sens[5].desc)); 158 sc->sc_sens[5].type = SENSOR_INTEGER; 159 sensor_add(&sc->sc_sens[5]); 160 sc->sc_sens[5].value = sc->sc_bst.bst_rate; 161 162 strlcpy(sc->sc_sens[6].desc, "remaining capacity", 163 sizeof(sc->sc_sens[6].desc)); 164 sc->sc_sens[6].type = type; 165 sensor_add(&sc->sc_sens[6]); 166 sc->sc_sens[6].value = sc->sc_bst.bst_capacity * 1000; 167 168 strlcpy(sc->sc_sens[7].desc, "current voltage", 169 sizeof(sc->sc_sens[7].desc)); 170 sc->sc_sens[7].type = SENSOR_VOLTS_DC; 171 sensor_add(&sc->sc_sens[7]); 172 sc->sc_sens[7].status = SENSOR_S_OK; 173 sc->sc_sens[7].value = sc->sc_bst.bst_voltage * 1000; 174} 175 176void 177acpibat_refresh(void *arg) 178{ 179 struct acpibat_softc *sc = arg; 180 181 dnprintf(30, "%s: %s: refresh\n", DEVNAME(sc), 182 sc->sc_devnode->parent->name); 183 184 acpibat_getbif(sc); 185 acpibat_getbst(sc); 186 187 rw_enter_write(&sc->sc_lock); 188 189 /* XXX ugh but make sure */ 190 if (!sc->sc_bif.bif_cap_granu1) 191 sc->sc_bif.bif_cap_granu1 = 1; 192 193 sc->sc_sens[0].value = sc->sc_bif.bif_last_capacity * 1000; 194 sc->sc_sens[1].value = sc->sc_bif.bif_warning * 1000; 195 sc->sc_sens[2].value = sc->sc_bif.bif_low * 1000; 196 sc->sc_sens[3].value = sc->sc_bif.bif_voltage * 1000; 197 198 sc->sc_sens[4].status = SENSOR_S_OK; 199 if (sc->sc_bst.bst_state & BST_DISCHARGE) 200 strlcpy(sc->sc_sens[4].desc, "battery discharging", 201 sizeof(sc->sc_sens[4].desc)); 202 else if (sc->sc_bst.bst_state & BST_CHARGE) 203 strlcpy(sc->sc_sens[4].desc, "battery charging", 204 sizeof(sc->sc_sens[4].desc)); 205 else if (sc->sc_bst.bst_state & BST_CRITICAL) { 206 strlcpy(sc->sc_sens[4].desc, "battery critical", 207 sizeof(sc->sc_sens[4].desc)); 208 sc->sc_sens[4].status = SENSOR_S_CRIT; 209 } 210 sc->sc_sens[4].value = sc->sc_bst.bst_state; 211 sc->sc_sens[5].value = sc->sc_bst.bst_rate; 212 sc->sc_sens[6].value = sc->sc_bst.bst_capacity * 1000; 213 sc->sc_sens[7].value = sc->sc_bst.bst_voltage * 1000; 214 215 rw_exit_write(&sc->sc_lock); 216} 217 218int 219acpibat_getbif(struct acpibat_softc *sc) 220{ 221 struct aml_value res; 222 int rv = 1; 223 224 rw_enter_write(&sc->sc_lock); 225 226 if (aml_evalname(sc->sc_acpi, sc->sc_devnode, "_STA", 0, NULL, &res) != 0) { 227 dnprintf(10, "%s: no _STA\n", 228 DEVNAME(sc)); 229 goto out; 230 } 231 aml_freevalue(&res); 232 233 if (aml_evalname(sc->sc_acpi, sc->sc_devnode, "_BIF", 0, NULL, &res) != 0) { 234 dnprintf(10, "%s: no _BIF\n", 235 DEVNAME(sc)); 236 printf("bif fails\n"); 237 goto out; 238 } 239 240 if (res.length != 13) { 241 printf("%s: invalid _BIF, battery information not saved\n", 242 DEVNAME(sc)); 243 goto out; 244 } 245 246 memset(&sc->sc_bif, 0, sizeof sc->sc_bif); 247 sc->sc_bif.bif_power_unit = aml_val2int(res.v_package[0]); 248 sc->sc_bif.bif_capacity = aml_val2int(res.v_package[1]); 249 sc->sc_bif.bif_last_capacity = aml_val2int(res.v_package[2]); 250 sc->sc_bif.bif_technology = aml_val2int(res.v_package[3]); 251 sc->sc_bif.bif_voltage = aml_val2int(res.v_package[4]); 252 sc->sc_bif.bif_warning = aml_val2int(res.v_package[5]); 253 sc->sc_bif.bif_low = aml_val2int(res.v_package[6]); 254 sc->sc_bif.bif_cap_granu1 = aml_val2int(res.v_package[7]); 255 sc->sc_bif.bif_cap_granu2 = aml_val2int(res.v_package[8]); 256 257 strlcpy(sc->sc_bif.bif_model, aml_strval(res.v_package[9]), 258 sizeof(sc->sc_bif.bif_model)); 259 strlcpy(sc->sc_bif.bif_serial, aml_strval(res.v_package[10]), 260 sizeof(sc->sc_bif.bif_serial)); 261 strlcpy(sc->sc_bif.bif_type, aml_strval(res.v_package[11]), 262 sizeof(sc->sc_bif.bif_type)); 263 strlcpy(sc->sc_bif.bif_oem, aml_strval(res.v_package[12]), 264 sizeof(sc->sc_bif.bif_oem)); 265 266 dnprintf(60, "power_unit: %u capacity: %u last_cap: %u tech: %u " 267 "volt: %u warn: %u low: %u gran1: %u gran2: %d model: %s " 268 "serial: %s type: %s oem: %s\n", 269 sc->sc_bif.bif_power_unit, 270 sc->sc_bif.bif_capacity, 271 sc->sc_bif.bif_last_capacity, 272 sc->sc_bif.bif_technology, 273 sc->sc_bif.bif_voltage, 274 sc->sc_bif.bif_warning, 275 sc->sc_bif.bif_low, 276 sc->sc_bif.bif_cap_granu1, 277 sc->sc_bif.bif_cap_granu2, 278 sc->sc_bif.bif_model, 279 sc->sc_bif.bif_serial, 280 sc->sc_bif.bif_type, 281 sc->sc_bif.bif_oem); 282 283out: 284 aml_freevalue(&res); 285 rw_exit_write(&sc->sc_lock); 286 return (rv); 287} 288 289int 290acpibat_getbst(struct acpibat_softc *sc) 291{ 292 struct aml_value res; 293 int rv = 0; 294 295 rw_enter_write(&sc->sc_lock); 296 297 if (aml_evalname(sc->sc_acpi, sc->sc_devnode, "_BST", 0, NULL, &res) != 0) { 298 dnprintf(10, "%s: no _BST\n", 299 DEVNAME(sc)); 300 printf("_bst fails\n"); 301 rv = EINVAL; 302 goto out; 303 } 304 305 if (res.length != 4) { 306 printf("%s: invalid _BST, battery status not saved\n", 307 DEVNAME(sc)); 308 rv = EINVAL; 309 goto out; 310 } 311 312 sc->sc_bst.bst_state = aml_val2int(res.v_package[0]); 313 sc->sc_bst.bst_rate = aml_val2int(res.v_package[1]); 314 sc->sc_bst.bst_capacity = aml_val2int(res.v_package[2]); 315 sc->sc_bst.bst_voltage = aml_val2int(res.v_package[3]); 316 aml_freevalue(&res); 317 318 dnprintf(60, "state: %u rate: %u cap: %u volt: %u ", 319 sc->sc_bst.bst_state, 320 sc->sc_bst.bst_rate, 321 sc->sc_bst.bst_capacity, 322 sc->sc_bst.bst_voltage); 323out: 324 rw_exit_write(&sc->sc_lock); 325 return (rv); 326} 327 328/* XXX it has been observed that some systems do not propagate battery 329 * insertion events up to the driver. What seems to happen is that DSDT 330 * does receive an interrupt however the originator bit is not set. 331 * This seems to happen when one inserts a 100% full battery. Removal 332 * of the power cord or insertion of a not 100% full battery breaks this 333 * behavior and all events will then be sent upwards. Currently there 334 * is no known work-around for it. 335 */ 336 337int 338acpibat_notify(struct aml_node *node, int notify_type, void *arg) 339{ 340 struct acpibat_softc *sc = arg; 341 342 dnprintf(10, "acpibat_notify: %.2x %s\n", notify_type, 343 sc->sc_devnode->parent->name); 344 345 switch (notify_type) { 346 case 0x80: /* _BST changed */ 347 if (sc->sc_bat_present == 0) { 348 printf("%s: %s: inserted\n", DEVNAME(sc), 349 sc->sc_devnode->parent->name); 350 351 if (sensor_task_register(sc, acpibat_refresh, 10)) 352 printf(", unable to register update task\n"); 353 354 sc->sc_bat_present = 1; 355 } 356 357 break; 358 case 0x81: /* _BIF changed */ 359 /* XXX consider this a device removal */ 360 if (sc->sc_bat_present != 0) { 361 sensor_task_unregister(sc); 362 363 strlcpy(sc->sc_sens[4].desc, "battery removed", 364 sizeof(sc->sc_sens[4].desc)); 365 printf("%s: %s: removed\n", DEVNAME(sc), 366 sc->sc_devnode->parent->name); 367 368 sc->sc_bat_present = 0; 369 } 370 break; 371 default: 372 printf("%s: unhandled battery event %x\n", DEVNAME(sc), 373 notify_type); 374 break; 375 } 376 377 acpibat_getbif(sc); 378 acpibat_getbst(sc); 379 acpibat_refresh(sc); 380 381 return (0); 382} 383