acpibat.c revision 1.37
1/* $OpenBSD: acpibat.c,v 1.37 2006/12/26 23:58:08 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/malloc.h> 23#include <sys/sensors.h> 24 25#include <machine/bus.h> 26 27#include <dev/acpi/acpireg.h> 28#include <dev/acpi/acpivar.h> 29#include <dev/acpi/acpidev.h> 30#include <dev/acpi/amltypes.h> 31#include <dev/acpi/dsdt.h> 32 33int acpibat_match(struct device *, void *, void *); 34void acpibat_attach(struct device *, struct device *, void *); 35 36struct cfattach acpibat_ca = { 37 sizeof(struct acpibat_softc), acpibat_match, acpibat_attach 38}; 39 40struct cfdriver acpibat_cd = { 41 NULL, "acpibat", DV_DULL 42}; 43 44void acpibat_monitor(struct acpibat_softc *); 45void acpibat_refresh(void *); 46int acpibat_getbif(struct acpibat_softc *); 47int acpibat_getbst(struct acpibat_softc *); 48int acpibat_notify(struct aml_node *, int, void *); 49 50int 51acpibat_match(struct device *parent, void *match, void *aux) 52{ 53 struct acpi_attach_args *aa = aux; 54 struct cfdata *cf = match; 55 56 /* sanity */ 57 if (aa->aaa_name == NULL || 58 strcmp(aa->aaa_name, cf->cf_driver->cd_name) != 0 || 59 aa->aaa_table != NULL) 60 return (0); 61 62 return (1); 63} 64 65void 66acpibat_attach(struct device *parent, struct device *self, void *aux) 67{ 68 struct acpibat_softc *sc = (struct acpibat_softc *)self; 69 struct acpi_attach_args *aa = aux; 70 struct aml_value res; 71 72 sc->sc_acpi = (struct acpi_softc *)parent; 73 sc->sc_devnode = aa->aaa_node->child; 74 75 if (aml_evalname(sc->sc_acpi, sc->sc_devnode, "_STA", 0, NULL, &res)) { 76 dnprintf(10, "%s: no _STA\n", DEVNAME(sc)); 77 return; 78 } 79 80 if ((sc->sc_bat_present = res.v_integer & STA_BATTERY) != 0) { 81 acpibat_getbif(sc); 82 acpibat_getbst(sc); 83 printf(": %s: model: %s serial: %s type: %s oem: %s\n", 84 sc->sc_devnode->parent->name, 85 sc->sc_bif.bif_model, 86 sc->sc_bif.bif_serial, 87 sc->sc_bif.bif_type, 88 sc->sc_bif.bif_oem); 89 } else 90 printf(": %s: not present\n", sc->sc_devnode->parent->name); 91 92 aml_freevalue(&res); 93 94 /* create sensors */ 95 acpibat_monitor(sc); 96 97 /* populate sensors */ 98 acpibat_refresh(sc); 99 100 aml_register_notify(sc->sc_devnode->parent, aa->aaa_dev, 101 acpibat_notify, sc, ACPIDEV_POLL); 102} 103 104void 105acpibat_monitor(struct acpibat_softc *sc) 106{ 107 int type; 108 109 /* assume _BIF and _BST have been called */ 110 strlcpy(sc->sc_sensdev.xname, DEVNAME(sc), 111 sizeof(sc->sc_sensdev.xname)); 112 113 type = sc->sc_bif.bif_power_unit ? SENSOR_AMPHOUR : SENSOR_WATTHOUR; 114 115 strlcpy(sc->sc_sens[0].desc, "last full capacity", 116 sizeof(sc->sc_sens[0].desc)); 117 sc->sc_sens[0].type = type; 118 sensor_attach(&sc->sc_sensdev, &sc->sc_sens[0]); 119 sc->sc_sens[0].value = sc->sc_bif.bif_last_capacity * 1000; 120 121 strlcpy(sc->sc_sens[1].desc, "warning capacity", 122 sizeof(sc->sc_sens[1].desc)); 123 sc->sc_sens[1].type = type; 124 sensor_attach(&sc->sc_sensdev, &sc->sc_sens[1]); 125 sc->sc_sens[1].value = sc->sc_bif.bif_warning * 1000; 126 127 strlcpy(sc->sc_sens[2].desc, "low capacity", 128 sizeof(sc->sc_sens[2].desc)); 129 sc->sc_sens[2].type = type; 130 sensor_attach(&sc->sc_sensdev, &sc->sc_sens[2]); 131 sc->sc_sens[2].value = sc->sc_bif.bif_low * 1000; 132 133 strlcpy(sc->sc_sens[3].desc, "voltage", sizeof(sc->sc_sens[3].desc)); 134 sc->sc_sens[3].type = SENSOR_VOLTS_DC; 135 sensor_attach(&sc->sc_sensdev, &sc->sc_sens[3]); 136 sc->sc_sens[3].status = SENSOR_S_OK; 137 sc->sc_sens[3].value = sc->sc_bif.bif_voltage * 1000; 138 139 strlcpy(sc->sc_sens[4].desc, "battery unknown", 140 sizeof(sc->sc_sens[4].desc)); 141 sc->sc_sens[4].type = SENSOR_INTEGER; 142 sensor_attach(&sc->sc_sensdev, &sc->sc_sens[4]); 143 sc->sc_sens[4].status = SENSOR_S_UNKNOWN; 144 sc->sc_sens[4].value = sc->sc_bst.bst_state; 145 146 strlcpy(sc->sc_sens[5].desc, "rate", sizeof(sc->sc_sens[5].desc)); 147 sc->sc_sens[5].type = SENSOR_INTEGER; 148 sensor_attach(&sc->sc_sensdev, &sc->sc_sens[5]); 149 sc->sc_sens[5].value = sc->sc_bst.bst_rate; 150 151 strlcpy(sc->sc_sens[6].desc, "remaining capacity", 152 sizeof(sc->sc_sens[6].desc)); 153 sc->sc_sens[6].type = type; 154 sensor_attach(&sc->sc_sensdev, &sc->sc_sens[6]); 155 sc->sc_sens[6].value = sc->sc_bst.bst_capacity * 1000; 156 157 strlcpy(sc->sc_sens[7].desc, "current voltage", 158 sizeof(sc->sc_sens[7].desc)); 159 sc->sc_sens[7].type = SENSOR_VOLTS_DC; 160 sensor_attach(&sc->sc_sensdev, &sc->sc_sens[7]); 161 sc->sc_sens[7].status = SENSOR_S_OK; 162 sc->sc_sens[7].value = sc->sc_bst.bst_voltage * 1000; 163 164 sensordev_install(&sc->sc_sensdev); 165} 166 167void 168acpibat_refresh(void *arg) 169{ 170 struct acpibat_softc *sc = arg; 171 int i; 172 173 dnprintf(30, "%s: %s: refresh\n", DEVNAME(sc), 174 sc->sc_devnode->parent->name); 175 176 if (!sc->sc_bat_present) { 177 for (i = 0; i < 8; i++) 178 sc->sc_sens[i].value = 0; 179 strlcpy(sc->sc_sens[4].desc, "battery removed", 180 sizeof(sc->sc_sens[4].desc)); 181 sc->sc_sens[4].status = SENSOR_S_OK; 182 return; 183 } 184 185 acpibat_getbif(sc); 186 acpibat_getbst(sc); 187 188 sc->sc_sens[0].value = sc->sc_bif.bif_last_capacity * 1000; 189 sc->sc_sens[1].value = sc->sc_bif.bif_warning * 1000; 190 sc->sc_sens[2].value = sc->sc_bif.bif_low * 1000; 191 sc->sc_sens[3].value = sc->sc_bif.bif_voltage * 1000; 192 193 sc->sc_sens[4].status = SENSOR_S_OK; 194 if (sc->sc_bst.bst_state & BST_DISCHARGE) 195 strlcpy(sc->sc_sens[4].desc, "battery discharging", 196 sizeof(sc->sc_sens[4].desc)); 197 else if (sc->sc_bst.bst_state & BST_CHARGE) 198 strlcpy(sc->sc_sens[4].desc, "battery charging", 199 sizeof(sc->sc_sens[4].desc)); 200 else if (sc->sc_bst.bst_state & BST_CRITICAL) { 201 strlcpy(sc->sc_sens[4].desc, "battery critical", 202 sizeof(sc->sc_sens[4].desc)); 203 sc->sc_sens[4].status = SENSOR_S_CRIT; 204 } else /* whenever there is no status the battery is full */ 205 strlcpy(sc->sc_sens[4].desc, "battery full", 206 sizeof(sc->sc_sens[4].desc)); 207 208 sc->sc_sens[4].value = sc->sc_bst.bst_state; 209 sc->sc_sens[5].value = sc->sc_bst.bst_rate; 210 sc->sc_sens[6].value = sc->sc_bst.bst_capacity * 1000; 211 sc->sc_sens[7].value = sc->sc_bst.bst_voltage * 1000; 212} 213 214int 215acpibat_getbif(struct acpibat_softc *sc) 216{ 217 struct aml_value res; 218 int rv = EINVAL; 219 220 if (aml_evalname(sc->sc_acpi, sc->sc_devnode, "_STA", 0, NULL, &res)) { 221 dnprintf(10, "%s: no _STA\n", DEVNAME(sc)); 222 goto out; 223 } 224 aml_freevalue(&res); 225 226 if (aml_evalname(sc->sc_acpi, sc->sc_devnode, "_BIF", 0, NULL, &res)) { 227 dnprintf(10, "%s: no _BIF\n", DEVNAME(sc)); 228 goto out; 229 } 230 231 if (res.length != 13) { 232 dnprintf(10, "%s: invalid _BIF, battery info not saved\n", 233 DEVNAME(sc)); 234 goto out; 235 } 236 237 memset(&sc->sc_bif, 0, sizeof sc->sc_bif); 238 sc->sc_bif.bif_power_unit = aml_val2int(res.v_package[0]); 239 sc->sc_bif.bif_capacity = aml_val2int(res.v_package[1]); 240 sc->sc_bif.bif_last_capacity = aml_val2int(res.v_package[2]); 241 sc->sc_bif.bif_technology = aml_val2int(res.v_package[3]); 242 sc->sc_bif.bif_voltage = aml_val2int(res.v_package[4]); 243 sc->sc_bif.bif_warning = aml_val2int(res.v_package[5]); 244 sc->sc_bif.bif_low = aml_val2int(res.v_package[6]); 245 sc->sc_bif.bif_cap_granu1 = aml_val2int(res.v_package[7]); 246 sc->sc_bif.bif_cap_granu2 = aml_val2int(res.v_package[8]); 247 248 strlcpy(sc->sc_bif.bif_model, aml_strval(res.v_package[9]), 249 sizeof(sc->sc_bif.bif_model)); 250 strlcpy(sc->sc_bif.bif_serial, aml_strval(res.v_package[10]), 251 sizeof(sc->sc_bif.bif_serial)); 252 strlcpy(sc->sc_bif.bif_type, aml_strval(res.v_package[11]), 253 sizeof(sc->sc_bif.bif_type)); 254 strlcpy(sc->sc_bif.bif_oem, aml_strval(res.v_package[12]), 255 sizeof(sc->sc_bif.bif_oem)); 256 257 dnprintf(60, "power_unit: %u capacity: %u last_cap: %u tech: %u " 258 "volt: %u warn: %u low: %u gran1: %u gran2: %d model: %s " 259 "serial: %s type: %s oem: %s\n", 260 sc->sc_bif.bif_power_unit, 261 sc->sc_bif.bif_capacity, 262 sc->sc_bif.bif_last_capacity, 263 sc->sc_bif.bif_technology, 264 sc->sc_bif.bif_voltage, 265 sc->sc_bif.bif_warning, 266 sc->sc_bif.bif_low, 267 sc->sc_bif.bif_cap_granu1, 268 sc->sc_bif.bif_cap_granu2, 269 sc->sc_bif.bif_model, 270 sc->sc_bif.bif_serial, 271 sc->sc_bif.bif_type, 272 sc->sc_bif.bif_oem); 273 274 rv = 0; 275out: 276 aml_freevalue(&res); 277 return (rv); 278} 279 280int 281acpibat_getbst(struct acpibat_softc *sc) 282{ 283 struct aml_value res; 284 int rv = EINVAL; 285 286 if (aml_evalname(sc->sc_acpi, sc->sc_devnode, "_BST", 0, NULL, &res)) { 287 dnprintf(10, "%s: no _BST\n", DEVNAME(sc)); 288 goto out; 289 } 290 291 if (res.length != 4) { 292 dnprintf(10, "%s: invalid _BST, battery status not saved\n", 293 DEVNAME(sc)); 294 goto out; 295 } 296 297 sc->sc_bst.bst_state = aml_val2int(res.v_package[0]); 298 sc->sc_bst.bst_rate = aml_val2int(res.v_package[1]); 299 sc->sc_bst.bst_capacity = aml_val2int(res.v_package[2]); 300 sc->sc_bst.bst_voltage = aml_val2int(res.v_package[3]); 301 302 dnprintf(60, "state: %u rate: %u cap: %u volt: %u ", 303 sc->sc_bst.bst_state, 304 sc->sc_bst.bst_rate, 305 sc->sc_bst.bst_capacity, 306 sc->sc_bst.bst_voltage); 307 308 rv = 0; 309out: 310 aml_freevalue(&res); 311 return (rv); 312} 313 314/* XXX it has been observed that some systems do not propagate battery 315 * insertion events up to the driver. What seems to happen is that DSDT 316 * does receive an interrupt however the originator bit is not set. 317 * This seems to happen when one inserts a 100% full battery. Removal 318 * of the power cord or insertion of a not 100% full battery breaks this 319 * behavior and all events will then be sent upwards. Currently there 320 * is no known work-around for it. 321 */ 322 323int 324acpibat_notify(struct aml_node *node, int notify_type, void *arg) 325{ 326 struct acpibat_softc *sc = arg; 327 328 dnprintf(10, "acpibat_notify: %.2x %s\n", notify_type, 329 sc->sc_devnode->parent->name); 330 331 switch (notify_type) { 332 case 0x80: /* _BST changed */ 333 if (!sc->sc_bat_present) { 334 printf("%s: %s: inserted\n", DEVNAME(sc), 335 sc->sc_devnode->parent->name); 336 sc->sc_bat_present = 1; 337 } 338 break; 339 case 0x81: /* _BIF changed */ 340 /* XXX consider this a device removal */ 341 if (sc->sc_bat_present) { 342 printf("%s: %s: removed\n", DEVNAME(sc), 343 sc->sc_devnode->parent->name); 344 sc->sc_bat_present = 0; 345 } 346 break; 347 default: 348 break; 349 } 350 351 acpibat_refresh(sc); 352 353 return (0); 354} 355