Annotation of sys/dev/acpi/acpibat.c, Revision 1.1
1.1 ! nbrk 1: /* $OpenBSD: acpibat.c,v 1.40 2007/03/20 15:17:21 mk 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:
! 33: int acpibat_match(struct device *, void *, void *);
! 34: void acpibat_attach(struct device *, struct device *, void *);
! 35:
! 36: struct cfattach acpibat_ca = {
! 37: sizeof(struct acpibat_softc), acpibat_match, acpibat_attach
! 38: };
! 39:
! 40: struct cfdriver acpibat_cd = {
! 41: NULL, "acpibat", DV_DULL
! 42: };
! 43:
! 44: void acpibat_monitor(struct acpibat_softc *);
! 45: void acpibat_refresh(void *);
! 46: int acpibat_getbif(struct acpibat_softc *);
! 47: int acpibat_getbst(struct acpibat_softc *);
! 48: int acpibat_notify(struct aml_node *, int, void *);
! 49:
! 50: int
! 51: acpibat_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:
! 65: void
! 66: acpibat_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 = aml_val2int(&res) & 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:
! 104: void
! 105: acpibat_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].value = sc->sc_bif.bif_voltage * 1000;
! 137:
! 138: strlcpy(sc->sc_sens[4].desc, "battery unknown",
! 139: sizeof(sc->sc_sens[4].desc));
! 140: sc->sc_sens[4].type = SENSOR_INTEGER;
! 141: sensor_attach(&sc->sc_sensdev, &sc->sc_sens[4]);
! 142: sc->sc_sens[4].value = sc->sc_bst.bst_state;
! 143:
! 144: strlcpy(sc->sc_sens[5].desc, "rate", sizeof(sc->sc_sens[5].desc));
! 145: sc->sc_sens[5].type = SENSOR_INTEGER;
! 146: sensor_attach(&sc->sc_sensdev, &sc->sc_sens[5]);
! 147: sc->sc_sens[5].value = sc->sc_bst.bst_rate;
! 148:
! 149: strlcpy(sc->sc_sens[6].desc, "remaining capacity",
! 150: sizeof(sc->sc_sens[6].desc));
! 151: sc->sc_sens[6].type = type;
! 152: sensor_attach(&sc->sc_sensdev, &sc->sc_sens[6]);
! 153: sc->sc_sens[6].value = sc->sc_bst.bst_capacity * 1000;
! 154:
! 155: strlcpy(sc->sc_sens[7].desc, "current voltage",
! 156: sizeof(sc->sc_sens[7].desc));
! 157: sc->sc_sens[7].type = SENSOR_VOLTS_DC;
! 158: sensor_attach(&sc->sc_sensdev, &sc->sc_sens[7]);
! 159: sc->sc_sens[7].value = sc->sc_bst.bst_voltage * 1000;
! 160:
! 161: sensordev_install(&sc->sc_sensdev);
! 162: }
! 163:
! 164: void
! 165: acpibat_refresh(void *arg)
! 166: {
! 167: struct acpibat_softc *sc = arg;
! 168: int i;
! 169:
! 170: dnprintf(30, "%s: %s: refresh\n", DEVNAME(sc),
! 171: sc->sc_devnode->parent->name);
! 172:
! 173: if (!sc->sc_bat_present) {
! 174: for (i = 0; i < 8; i++) {
! 175: sc->sc_sens[i].value = 0;
! 176: sc->sc_sens[i].status = SENSOR_S_UNSPEC;
! 177: sc->sc_sens[i].flags = SENSOR_FINVALID;
! 178: }
! 179: /* override state */
! 180: strlcpy(sc->sc_sens[4].desc, "battery removed",
! 181: sizeof(sc->sc_sens[4].desc));
! 182: return;
! 183: }
! 184:
! 185: /*
! 186: * XXX don't really need _BIF but keep it here in case we
! 187: * miss an insertion/removal event
! 188: */
! 189: acpibat_getbif(sc);
! 190: acpibat_getbst(sc);
! 191:
! 192: /* _BIF values are static, sensor 0..3 */
! 193: if (sc->sc_bif.bif_last_capacity == BIF_UNKNOWN) {
! 194: sc->sc_sens[0].value = 0;
! 195: sc->sc_sens[0].status = SENSOR_S_UNKNOWN;
! 196: sc->sc_sens[0].flags = SENSOR_FUNKNOWN;
! 197: } else {
! 198: sc->sc_sens[0].value = sc->sc_bif.bif_last_capacity * 1000;
! 199: sc->sc_sens[0].status = SENSOR_S_UNSPEC;
! 200: sc->sc_sens[0].flags = 0;
! 201: }
! 202: sc->sc_sens[1].value = sc->sc_bif.bif_warning * 1000;
! 203: sc->sc_sens[1].flags = 0;
! 204: sc->sc_sens[2].value = sc->sc_bif.bif_low * 1000;
! 205: sc->sc_sens[2].flags = 0;
! 206: if (sc->sc_bif.bif_voltage == BIF_UNKNOWN) {
! 207: sc->sc_sens[3].value = 0;
! 208: sc->sc_sens[3].status = SENSOR_S_UNKNOWN;
! 209: sc->sc_sens[3].flags = SENSOR_FUNKNOWN;
! 210: } else {
! 211: sc->sc_sens[3].value = sc->sc_bif.bif_voltage * 1000;
! 212: sc->sc_sens[3].status = SENSOR_S_UNSPEC;
! 213: sc->sc_sens[3].flags = 0;
! 214: }
! 215:
! 216: /* _BST values are dynamic, sensor 4..7 */
! 217: sc->sc_sens[4].status = SENSOR_S_OK;
! 218: sc->sc_sens[4].flags = 0;
! 219: if (sc->sc_bif.bif_last_capacity == BIF_UNKNOWN ||
! 220: sc->sc_bst.bst_capacity == BST_UNKNOWN) {
! 221: sc->sc_sens[4].status = SENSOR_S_UNKNOWN;
! 222: sc->sc_sens[4].flags = SENSOR_FUNKNOWN;
! 223: strlcpy(sc->sc_sens[4].desc, "battery unknown",
! 224: sizeof(sc->sc_sens[4].desc));
! 225: } else if (sc->sc_bst.bst_capacity >= sc->sc_bif.bif_last_capacity)
! 226: strlcpy(sc->sc_sens[4].desc, "battery full",
! 227: sizeof(sc->sc_sens[4].desc));
! 228: else if (sc->sc_bst.bst_state & BST_DISCHARGE)
! 229: strlcpy(sc->sc_sens[4].desc, "battery discharging",
! 230: sizeof(sc->sc_sens[4].desc));
! 231: else if (sc->sc_bst.bst_state & BST_CHARGE)
! 232: strlcpy(sc->sc_sens[4].desc, "battery charging",
! 233: sizeof(sc->sc_sens[4].desc));
! 234: else if (sc->sc_bst.bst_state & BST_CRITICAL) {
! 235: strlcpy(sc->sc_sens[4].desc, "battery critical",
! 236: sizeof(sc->sc_sens[4].desc));
! 237: sc->sc_sens[4].status = SENSOR_S_CRIT;
! 238: } else
! 239: strlcpy(sc->sc_sens[4].desc, "battery idle",
! 240: sizeof(sc->sc_sens[4].desc));
! 241: sc->sc_sens[4].value = sc->sc_bst.bst_state;
! 242:
! 243: if (sc->sc_bst.bst_rate == BST_UNKNOWN) {
! 244: sc->sc_sens[5].value = 0;
! 245: sc->sc_sens[5].status = SENSOR_S_UNKNOWN;
! 246: sc->sc_sens[5].flags = SENSOR_FUNKNOWN;
! 247: } else {
! 248: sc->sc_sens[5].value = sc->sc_bst.bst_rate;
! 249: sc->sc_sens[5].status = SENSOR_S_UNSPEC;
! 250: sc->sc_sens[5].flags = 0;
! 251: }
! 252:
! 253: if (sc->sc_bst.bst_capacity == BST_UNKNOWN) {
! 254: sc->sc_sens[6].value = 0;
! 255: sc->sc_sens[6].status = SENSOR_S_UNKNOWN;
! 256: sc->sc_sens[6].flags = SENSOR_FUNKNOWN;
! 257: } else {
! 258: sc->sc_sens[6].value = sc->sc_bst.bst_capacity * 1000;
! 259: sc->sc_sens[6].flags = 0;
! 260:
! 261: if (sc->sc_bst.bst_capacity < sc->sc_bif.bif_low)
! 262: /* XXX we should shutdown the system */
! 263: sc->sc_sens[6].status = SENSOR_S_CRIT;
! 264: else if (sc->sc_bst.bst_capacity < sc->sc_bif.bif_warning)
! 265: sc->sc_sens[6].status = SENSOR_S_WARN;
! 266: else
! 267: sc->sc_sens[6].status = SENSOR_S_OK;
! 268: }
! 269:
! 270: if(sc->sc_bst.bst_voltage == BST_UNKNOWN) {
! 271: sc->sc_sens[7].value = 0;
! 272: sc->sc_sens[7].status = SENSOR_S_UNKNOWN;
! 273: sc->sc_sens[7].flags = SENSOR_FUNKNOWN;
! 274: } else {
! 275: sc->sc_sens[7].value = sc->sc_bst.bst_voltage * 1000;
! 276: sc->sc_sens[7].status = SENSOR_S_UNSPEC;
! 277: sc->sc_sens[7].flags = 0;
! 278: }
! 279: }
! 280:
! 281: int
! 282: acpibat_getbif(struct acpibat_softc *sc)
! 283: {
! 284: struct aml_value res;
! 285: int rv = EINVAL;
! 286:
! 287: if (aml_evalname(sc->sc_acpi, sc->sc_devnode, "_STA", 0, NULL, &res)) {
! 288: dnprintf(10, "%s: no _STA\n", DEVNAME(sc));
! 289: goto out;
! 290: }
! 291: aml_freevalue(&res);
! 292:
! 293: if (aml_evalname(sc->sc_acpi, sc->sc_devnode, "_BIF", 0, NULL, &res)) {
! 294: dnprintf(10, "%s: no _BIF\n", DEVNAME(sc));
! 295: goto out;
! 296: }
! 297:
! 298: if (res.length != 13) {
! 299: dnprintf(10, "%s: invalid _BIF, battery info not saved\n",
! 300: DEVNAME(sc));
! 301: goto out;
! 302: }
! 303:
! 304: memset(&sc->sc_bif, 0, sizeof sc->sc_bif);
! 305: sc->sc_bif.bif_power_unit = aml_val2int(res.v_package[0]);
! 306: sc->sc_bif.bif_capacity = aml_val2int(res.v_package[1]);
! 307: sc->sc_bif.bif_last_capacity = aml_val2int(res.v_package[2]);
! 308: sc->sc_bif.bif_technology = aml_val2int(res.v_package[3]);
! 309: sc->sc_bif.bif_voltage = aml_val2int(res.v_package[4]);
! 310: sc->sc_bif.bif_warning = aml_val2int(res.v_package[5]);
! 311: sc->sc_bif.bif_low = aml_val2int(res.v_package[6]);
! 312: sc->sc_bif.bif_cap_granu1 = aml_val2int(res.v_package[7]);
! 313: sc->sc_bif.bif_cap_granu2 = aml_val2int(res.v_package[8]);
! 314:
! 315: strlcpy(sc->sc_bif.bif_model, aml_strval(res.v_package[9]),
! 316: sizeof(sc->sc_bif.bif_model));
! 317: strlcpy(sc->sc_bif.bif_serial, aml_strval(res.v_package[10]),
! 318: sizeof(sc->sc_bif.bif_serial));
! 319: strlcpy(sc->sc_bif.bif_type, aml_strval(res.v_package[11]),
! 320: sizeof(sc->sc_bif.bif_type));
! 321: strlcpy(sc->sc_bif.bif_oem, aml_strval(res.v_package[12]),
! 322: sizeof(sc->sc_bif.bif_oem));
! 323:
! 324: dnprintf(60, "power_unit: %u capacity: %u last_cap: %u tech: %u "
! 325: "volt: %u warn: %u low: %u gran1: %u gran2: %d model: %s "
! 326: "serial: %s type: %s oem: %s\n",
! 327: sc->sc_bif.bif_power_unit,
! 328: sc->sc_bif.bif_capacity,
! 329: sc->sc_bif.bif_last_capacity,
! 330: sc->sc_bif.bif_technology,
! 331: sc->sc_bif.bif_voltage,
! 332: sc->sc_bif.bif_warning,
! 333: sc->sc_bif.bif_low,
! 334: sc->sc_bif.bif_cap_granu1,
! 335: sc->sc_bif.bif_cap_granu2,
! 336: sc->sc_bif.bif_model,
! 337: sc->sc_bif.bif_serial,
! 338: sc->sc_bif.bif_type,
! 339: sc->sc_bif.bif_oem);
! 340:
! 341: rv = 0;
! 342: out:
! 343: aml_freevalue(&res);
! 344: return (rv);
! 345: }
! 346:
! 347: int
! 348: acpibat_getbst(struct acpibat_softc *sc)
! 349: {
! 350: struct aml_value res;
! 351: int rv = EINVAL;
! 352:
! 353: if (aml_evalname(sc->sc_acpi, sc->sc_devnode, "_BST", 0, NULL, &res)) {
! 354: dnprintf(10, "%s: no _BST\n", DEVNAME(sc));
! 355: goto out;
! 356: }
! 357:
! 358: if (res.length != 4) {
! 359: dnprintf(10, "%s: invalid _BST, battery status not saved\n",
! 360: DEVNAME(sc));
! 361: goto out;
! 362: }
! 363:
! 364: sc->sc_bst.bst_state = aml_val2int(res.v_package[0]);
! 365: sc->sc_bst.bst_rate = aml_val2int(res.v_package[1]);
! 366: sc->sc_bst.bst_capacity = aml_val2int(res.v_package[2]);
! 367: sc->sc_bst.bst_voltage = aml_val2int(res.v_package[3]);
! 368:
! 369: dnprintf(60, "state: %u rate: %u cap: %u volt: %u ",
! 370: sc->sc_bst.bst_state,
! 371: sc->sc_bst.bst_rate,
! 372: sc->sc_bst.bst_capacity,
! 373: sc->sc_bst.bst_voltage);
! 374:
! 375: rv = 0;
! 376: out:
! 377: aml_freevalue(&res);
! 378: return (rv);
! 379: }
! 380:
! 381: /* XXX it has been observed that some systems do not propagate battery
! 382: * insertion events up to the driver. What seems to happen is that DSDT
! 383: * does receive an interrupt however the originator bit is not set.
! 384: * This seems to happen when one inserts a 100% full battery. Removal
! 385: * of the power cord or insertion of a not 100% full battery breaks this
! 386: * behavior and all events will then be sent upwards. Currently there
! 387: * is no known work-around for it.
! 388: */
! 389:
! 390: int
! 391: acpibat_notify(struct aml_node *node, int notify_type, void *arg)
! 392: {
! 393: struct acpibat_softc *sc = arg;
! 394:
! 395: dnprintf(10, "acpibat_notify: %.2x %s\n", notify_type,
! 396: sc->sc_devnode->parent->name);
! 397:
! 398: switch (notify_type) {
! 399: case 0x80: /* _BST changed */
! 400: if (!sc->sc_bat_present) {
! 401: printf("%s: %s: inserted\n", DEVNAME(sc),
! 402: sc->sc_devnode->parent->name);
! 403: sc->sc_bat_present = 1;
! 404: }
! 405: break;
! 406: case 0x81: /* _BIF changed */
! 407: /* XXX consider this a device removal */
! 408: if (sc->sc_bat_present) {
! 409: printf("%s: %s: removed\n", DEVNAME(sc),
! 410: sc->sc_devnode->parent->name);
! 411: sc->sc_bat_present = 0;
! 412: }
! 413: break;
! 414: default:
! 415: break;
! 416: }
! 417:
! 418: acpibat_refresh(sc);
! 419:
! 420: return (0);
! 421: }
CVSweb