1 /* $NetBSD: acpi_bat.c,v 1.43 2006/02/20 12:17:49 kochi Exp $ */ 2 3 /*- 4 * Copyright (c) 2003 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Charles M. Hannum of By Noon Software, Inc. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 3. All advertising materials mentioning features or use of this software 19 * must display the following acknowledgement: 20 * This product includes software developed by the NetBSD 21 * Foundation, Inc. and its contributors. 22 * 4. Neither the name of The NetBSD Foundation nor the names of its 23 * contributors may be used to endorse or promote products derived 24 * from this software without specific prior written permission. 25 * 26 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 27 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 28 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 29 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 30 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 31 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 32 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 33 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 34 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 36 * POSSIBILITY OF SUCH DAMAGE. 37 */ 38 39 /* 40 * Copyright 2001 Bill Sommerfeld. 41 * All rights reserved. 42 * 43 * Redistribution and use in source and binary forms, with or without 44 * modification, are permitted provided that the following conditions 45 * are met: 46 * 1. Redistributions of source code must retain the above copyright 47 * notice, this list of conditions and the following disclaimer. 48 * 2. Redistributions in binary form must reproduce the above copyright 49 * notice, this list of conditions and the following disclaimer in the 50 * documentation and/or other materials provided with the distribution. 51 * 3. All advertising materials mentioning features or use of this software 52 * must display the following acknowledgement: 53 * This product includes software developed for the NetBSD Project by 54 * Wasabi Systems, Inc. 55 * 4. The name of Wasabi Systems, Inc. may not be used to endorse 56 * or promote products derived from this software without specific prior 57 * written permission. 58 * 59 * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND 60 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 61 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 62 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL WASABI SYSTEMS, INC 63 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 64 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 65 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 66 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 67 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 68 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 69 * POSSIBILITY OF SUCH DAMAGE. 70 */ 71 72 #if 0 73 #define ACPI_BAT_DEBUG 74 #endif 75 76 /* 77 * ACPI Battery Driver. 78 * 79 * ACPI defines two different battery device interfaces: "Control 80 * Method" batteries, in which AML methods are defined in order to get 81 * battery status and set battery alarm thresholds, and a "Smart 82 * Battery" device, which is an SMbus device accessed through the ACPI 83 * Embedded Controller device. 84 * 85 * This driver is for the "Control Method"-style battery only. 86 */ 87 88 #include <sys/cdefs.h> 89 __KERNEL_RCSID(0, "$NetBSD: acpi_bat.c,v 1.43 2006/02/20 12:17:49 kochi Exp $"); 90 91 #include <sys/param.h> 92 #include <sys/systm.h> 93 #include <sys/kernel.h> /* for hz */ 94 #include <sys/device.h> 95 #include <dev/sysmon/sysmonvar.h> 96 97 #include <dev/acpi/acpica.h> 98 #include <dev/acpi/acpireg.h> 99 #include <dev/acpi/acpivar.h> 100 101 /* sensor indexes */ 102 #define ACPIBAT_PRESENT 0 103 #define ACPIBAT_DCAPACITY 1 104 #define ACPIBAT_LFCCAPACITY 2 105 #define ACPIBAT_TECHNOLOGY 3 106 #define ACPIBAT_DVOLTAGE 4 107 #define ACPIBAT_WCAPACITY 5 108 #define ACPIBAT_LCAPACITY 6 109 #define ACPIBAT_VOLTAGE 7 110 #define ACPIBAT_CHARGERATE 8 111 #define ACPIBAT_DISCHARGERATE 9 112 #define ACPIBAT_CAPACITY 10 113 #define ACPIBAT_CHARGING 11 114 #define ACPIBAT_DISCHARGING 12 115 #define ACPIBAT_NSENSORS 13 /* number of sensors */ 116 117 static const struct envsys_range acpibat_range_amp[] = { 118 { 0, 1, ENVSYS_SVOLTS_DC }, 119 { 1, 2, ENVSYS_SAMPS }, 120 { 2, 3, ENVSYS_SAMPHOUR }, 121 { 1, 0, -1 }, 122 }; 123 124 static const struct envsys_range acpibat_range_watt[] = { 125 { 0, 1, ENVSYS_SVOLTS_DC }, 126 { 1, 2, ENVSYS_SWATTS }, 127 { 2, 3, ENVSYS_SWATTHOUR }, 128 { 1, 0, -1 }, 129 }; 130 131 struct acpibat_softc { 132 struct device sc_dev; /* base device glue */ 133 struct acpi_devnode *sc_node; /* our ACPI devnode */ 134 int sc_flags; /* see below */ 135 int sc_available; /* available information level */ 136 137 struct sysmon_envsys sc_sysmon; 138 struct envsys_basic_info sc_info[ACPIBAT_NSENSORS]; 139 struct envsys_tre_data sc_data[ACPIBAT_NSENSORS]; 140 141 struct simplelock sc_lock; 142 143 struct timeval sc_lastupdate, sc_updateinterval; 144 }; 145 146 static const char * const bat_hid[] = { 147 "PNP0C0A", 148 NULL 149 }; 150 151 /* 152 * These flags are used to examine the battery device data returned from 153 * the ACPI interface, specifically the "battery status" 154 */ 155 #define ACPIBAT_PWRUNIT_MA 0x00000001 /* mA not mW */ 156 157 /* 158 * These flags are used to examine the battery charge/discharge/critical 159 * state returned from a get-status command. 160 */ 161 #define ACPIBAT_ST_DISCHARGING 0x00000001 /* battery is discharging */ 162 #define ACPIBAT_ST_CHARGING 0x00000002 /* battery is charging */ 163 #define ACPIBAT_ST_CRITICAL 0x00000004 /* battery is critical */ 164 165 /* 166 * Flags for battery status from _STA return 167 */ 168 #define ACPIBAT_STA_PRESENT 0x00000010 /* battery present */ 169 170 /* 171 * These flags are used to set internal state in our softc. 172 */ 173 #define ABAT_F_VERBOSE 0x01 /* verbose events */ 174 #define ABAT_F_PWRUNIT_MA 0x02 /* mA instead of mW */ 175 #define ABAT_F_PRESENT 0x04 /* is the battery present? */ 176 #define ABAT_F_LOCKED 0x08 /* is locked? */ 177 178 #define ABAT_SET(sc, f) (void)((sc)->sc_flags |= (f)) 179 #define ABAT_CLEAR(sc, f) (void)((sc)->sc_flags &= ~(f)) 180 #define ABAT_ISSET(sc, f) ((sc)->sc_flags & (f)) 181 182 /* 183 * Available info level 184 */ 185 186 #define ABAT_ALV_NONE 0 /* none is available */ 187 #define ABAT_ALV_PRESENCE 1 /* presence info is available */ 188 #define ABAT_ALV_INFO 2 /* battery info is available */ 189 #define ABAT_ALV_STAT 3 /* battery status is available */ 190 191 #define ABAT_ASSERT_LOCKED(sc) \ 192 do { \ 193 if (!((sc)->sc_flags & ABAT_F_LOCKED)) \ 194 panic("acpi_bat (expected to be locked)"); \ 195 } while(/*CONSTCOND*/0) 196 #define ABAT_ASSERT_UNLOCKED(sc) \ 197 do { \ 198 if (((sc)->sc_flags & ABAT_F_LOCKED)) \ 199 panic("acpi_bat (expected to be unlocked)"); \ 200 } while(/*CONSTCOND*/0) 201 #define ABAT_LOCK(sc, s) \ 202 do { \ 203 ABAT_ASSERT_UNLOCKED(sc); \ 204 (s) = splhigh(); \ 205 simple_lock(&(sc)->sc_lock); \ 206 ABAT_SET((sc), ABAT_F_LOCKED); \ 207 } while(/*CONSTCOND*/0) 208 #define ABAT_UNLOCK(sc, s) \ 209 do { \ 210 ABAT_ASSERT_LOCKED(sc); \ 211 ABAT_CLEAR((sc), ABAT_F_LOCKED); \ 212 simple_unlock(&(sc)->sc_lock); \ 213 splx((s)); \ 214 } while(/*CONSTCOND*/0) 215 216 static int acpibat_match(struct device *, struct cfdata *, void *); 217 static void acpibat_attach(struct device *, struct device *, void *); 218 219 CFATTACH_DECL(acpibat, sizeof(struct acpibat_softc), 220 acpibat_match, acpibat_attach, NULL, NULL); 221 222 static void acpibat_clear_presence(struct acpibat_softc *); 223 static void acpibat_clear_info(struct acpibat_softc *); 224 static void acpibat_clear_stat(struct acpibat_softc *); 225 static int acpibat_battery_present(struct acpibat_softc *); 226 static ACPI_STATUS acpibat_get_status(struct acpibat_softc *); 227 static ACPI_STATUS acpibat_get_info(struct acpibat_softc *); 228 static void acpibat_print_info(struct acpibat_softc *); 229 static void acpibat_print_stat(struct acpibat_softc *); 230 static void acpibat_update(void *); 231 232 static void acpibat_init_envsys(struct acpibat_softc *); 233 static void acpibat_notify_handler(ACPI_HANDLE, UINT32, void *); 234 static int acpibat_gtredata(struct sysmon_envsys *, struct envsys_tre_data *); 235 static int acpibat_streinfo(struct sysmon_envsys *, struct envsys_basic_info *); 236 237 /* 238 * acpibat_match: 239 * 240 * Autoconfiguration `match' routine. 241 */ 242 static int 243 acpibat_match(struct device *parent, struct cfdata *match, void *aux) 244 { 245 struct acpi_attach_args *aa = aux; 246 247 if (aa->aa_node->ad_type != ACPI_TYPE_DEVICE) 248 return 0; 249 250 return acpi_match_hid(aa->aa_node->ad_devinfo, bat_hid); 251 } 252 253 /* 254 * acpibat_attach: 255 * 256 * Autoconfiguration `attach' routine. 257 */ 258 static void 259 acpibat_attach(struct device *parent, struct device *self, void *aux) 260 { 261 struct acpibat_softc *sc = (void *) self; 262 struct acpi_attach_args *aa = aux; 263 ACPI_STATUS rv; 264 265 aprint_naive(": ACPI Battery (Control Method)\n"); 266 aprint_normal(": ACPI Battery (Control Method)\n"); 267 268 sc->sc_node = aa->aa_node; 269 simple_lock_init(&sc->sc_lock); 270 271 rv = AcpiInstallNotifyHandler(sc->sc_node->ad_handle, 272 ACPI_DEVICE_NOTIFY, 273 acpibat_notify_handler, sc); 274 if (ACPI_FAILURE(rv)) { 275 aprint_error("%s: unable to register DEVICE NOTIFY handler: %s\n", 276 sc->sc_dev.dv_xname, AcpiFormatException(rv)); 277 return; 278 } 279 280 /* XXX See acpibat_notify_handler() */ 281 rv = AcpiInstallNotifyHandler(sc->sc_node->ad_handle, 282 ACPI_SYSTEM_NOTIFY, 283 acpibat_notify_handler, sc); 284 if (ACPI_FAILURE(rv)) { 285 aprint_error("%s: unable to register SYSTEM NOTIFY handler: %s\n", 286 sc->sc_dev.dv_xname, AcpiFormatException(rv)); 287 return; 288 } 289 290 #ifdef ACPI_BAT_DEBUG 291 ABAT_SET(sc, ABAT_F_VERBOSE); 292 #endif 293 294 acpibat_init_envsys(sc); 295 } 296 297 /* 298 * clear informations 299 */ 300 301 static void 302 acpibat_clear_presence(struct acpibat_softc *sc) 303 { 304 305 ABAT_ASSERT_LOCKED(sc); 306 307 acpibat_clear_info(sc); 308 sc->sc_available = ABAT_ALV_NONE; 309 ABAT_CLEAR(sc, ABAT_F_PRESENT); 310 } 311 312 static void 313 acpibat_clear_info(struct acpibat_softc *sc) 314 { 315 316 ABAT_ASSERT_LOCKED(sc); 317 318 acpibat_clear_stat(sc); 319 if (sc->sc_available>ABAT_ALV_PRESENCE) 320 sc->sc_available = ABAT_ALV_PRESENCE; 321 sc->sc_data[ACPIBAT_DCAPACITY].validflags &= ~ENVSYS_FCURVALID; 322 sc->sc_data[ACPIBAT_LFCCAPACITY].validflags &= ~ENVSYS_FCURVALID; 323 sc->sc_data[ACPIBAT_CAPACITY].validflags &= ~ENVSYS_FMAXVALID; 324 sc->sc_data[ACPIBAT_TECHNOLOGY].validflags &= ~ENVSYS_FCURVALID; 325 sc->sc_data[ACPIBAT_DVOLTAGE].validflags &= ~ENVSYS_FCURVALID; 326 sc->sc_data[ACPIBAT_WCAPACITY].validflags &= ~(ENVSYS_FCURVALID | ENVSYS_FMAXVALID | ENVSYS_FFRACVALID); 327 sc->sc_data[ACPIBAT_LCAPACITY].validflags &= ~(ENVSYS_FCURVALID | ENVSYS_FMAXVALID | ENVSYS_FFRACVALID); 328 } 329 330 static void 331 acpibat_clear_stat(struct acpibat_softc *sc) 332 { 333 334 ABAT_ASSERT_LOCKED(sc); 335 336 if (sc->sc_available>ABAT_ALV_INFO) 337 sc->sc_available = ABAT_ALV_INFO; 338 sc->sc_data[ACPIBAT_CHARGERATE].validflags &= ~ENVSYS_FCURVALID; 339 sc->sc_data[ACPIBAT_DISCHARGERATE].validflags &= ~ENVSYS_FCURVALID; 340 sc->sc_data[ACPIBAT_CAPACITY].validflags &= ~(ENVSYS_FCURVALID | ENVSYS_FFRACVALID); 341 sc->sc_data[ACPIBAT_CAPACITY].warnflags = 0; 342 sc->sc_data[ACPIBAT_VOLTAGE].validflags &= ~ENVSYS_FCURVALID; 343 sc->sc_data[ACPIBAT_CHARGING].validflags &= ~ENVSYS_FCURVALID; 344 sc->sc_data[ACPIBAT_DISCHARGING].validflags &= ~ENVSYS_FCURVALID; 345 } 346 347 348 /* 349 * returns 0 for no battery, 1 for present, and -1 on error 350 */ 351 static int 352 acpibat_battery_present(struct acpibat_softc *sc) 353 { 354 u_int32_t sta; 355 int s; 356 ACPI_INTEGER val; 357 ACPI_STATUS rv; 358 359 rv = acpi_eval_integer(sc->sc_node->ad_handle, "_STA", &val); 360 if (ACPI_FAILURE(rv)) { 361 printf("%s: failed to evaluate _STA: %s\n", 362 sc->sc_dev.dv_xname, AcpiFormatException(rv)); 363 return -1; 364 } 365 366 sta = (u_int32_t)val; 367 368 ABAT_LOCK(sc, s); 369 sc->sc_available = ABAT_ALV_PRESENCE; 370 if (sta & ACPIBAT_STA_PRESENT) { 371 ABAT_SET(sc, ABAT_F_PRESENT); 372 sc->sc_data[ACPIBAT_PRESENT].cur.data_s = 1; 373 } else 374 sc->sc_data[ACPIBAT_PRESENT].cur.data_s = 0; 375 sc->sc_data[ACPIBAT_PRESENT].validflags |= ENVSYS_FCURVALID; 376 ABAT_UNLOCK(sc, s); 377 378 return (sta & ACPIBAT_STA_PRESENT) ? 1 : 0; 379 } 380 381 /* 382 * acpibat_get_info 383 * 384 * Get, and possibly display, the battery info. 385 */ 386 387 static ACPI_STATUS 388 acpibat_get_info(struct acpibat_softc *sc) 389 { 390 ACPI_OBJECT *p1, *p2; 391 ACPI_STATUS rv; 392 ACPI_BUFFER buf; 393 int capunit, rateunit, s; 394 395 rv = acpi_eval_struct(sc->sc_node->ad_handle, "_BIF", &buf); 396 if (ACPI_FAILURE(rv)) { 397 printf("%s: failed to evaluate _BIF: %s\n", 398 sc->sc_dev.dv_xname, AcpiFormatException(rv)); 399 return rv; 400 } 401 p1 = (ACPI_OBJECT *)buf.Pointer; 402 403 if (p1->Type != ACPI_TYPE_PACKAGE) { 404 printf("%s: expected PACKAGE, got %d\n", sc->sc_dev.dv_xname, 405 p1->Type); 406 goto out; 407 } 408 if (p1->Package.Count < 13) { 409 printf("%s: expected 13 elts, got %d\n", 410 sc->sc_dev.dv_xname, p1->Package.Count); 411 goto out; 412 } 413 p2 = p1->Package.Elements; 414 415 ABAT_LOCK(sc, s); 416 if ((p2[0].Integer.Value & ACPIBAT_PWRUNIT_MA) != 0) { 417 ABAT_SET(sc, ABAT_F_PWRUNIT_MA); 418 sc->sc_sysmon.sme_ranges = acpibat_range_amp; 419 capunit = ENVSYS_SAMPHOUR; 420 rateunit = ENVSYS_SAMPS; 421 } else { 422 ABAT_CLEAR(sc, ABAT_F_PWRUNIT_MA); 423 sc->sc_sysmon.sme_ranges = acpibat_range_watt; 424 capunit = ENVSYS_SWATTHOUR; 425 rateunit = ENVSYS_SWATTS; 426 } 427 428 #define INITDATA(index, unit) \ 429 sc->sc_data[index].units = unit; \ 430 sc->sc_info[index].units = unit; 431 432 INITDATA(ACPIBAT_DCAPACITY, capunit); 433 INITDATA(ACPIBAT_LFCCAPACITY, capunit); 434 INITDATA(ACPIBAT_WCAPACITY, capunit); 435 INITDATA(ACPIBAT_LCAPACITY, capunit); 436 INITDATA(ACPIBAT_CHARGERATE, rateunit); 437 INITDATA(ACPIBAT_DISCHARGERATE, rateunit); 438 INITDATA(ACPIBAT_CAPACITY, capunit); 439 440 #undef INITDATA 441 442 sc->sc_data[ACPIBAT_DCAPACITY].cur.data_s = p2[1].Integer.Value * 1000; 443 sc->sc_data[ACPIBAT_DCAPACITY].validflags |= ENVSYS_FCURVALID; 444 sc->sc_data[ACPIBAT_LFCCAPACITY].cur.data_s = p2[2].Integer.Value * 1000; 445 sc->sc_data[ACPIBAT_LFCCAPACITY].validflags |= ENVSYS_FCURVALID; 446 sc->sc_data[ACPIBAT_CAPACITY].max.data_s = p2[2].Integer.Value * 1000; 447 sc->sc_data[ACPIBAT_CAPACITY].validflags |= ENVSYS_FMAXVALID; 448 sc->sc_data[ACPIBAT_TECHNOLOGY].cur.data_s = p2[3].Integer.Value; 449 sc->sc_data[ACPIBAT_TECHNOLOGY].validflags |= ENVSYS_FCURVALID; 450 sc->sc_data[ACPIBAT_DVOLTAGE].cur.data_s = p2[4].Integer.Value * 1000; 451 sc->sc_data[ACPIBAT_DVOLTAGE].validflags |= ENVSYS_FCURVALID; 452 sc->sc_data[ACPIBAT_WCAPACITY].cur.data_s = p2[5].Integer.Value * 1000; 453 sc->sc_data[ACPIBAT_WCAPACITY].max.data_s = p2[2].Integer.Value * 1000; 454 sc->sc_data[ACPIBAT_WCAPACITY].validflags |= ENVSYS_FCURVALID | ENVSYS_FMAXVALID | ENVSYS_FFRACVALID; 455 sc->sc_data[ACPIBAT_LCAPACITY].cur.data_s = p2[6].Integer.Value * 1000; 456 sc->sc_data[ACPIBAT_LCAPACITY].max.data_s = p2[2].Integer.Value * 1000; 457 sc->sc_data[ACPIBAT_LCAPACITY].validflags |= ENVSYS_FCURVALID | ENVSYS_FMAXVALID | ENVSYS_FFRACVALID; 458 sc->sc_available = ABAT_ALV_INFO; 459 ABAT_UNLOCK(sc, s); 460 461 printf("%s: battery info: %s, %s, %s, %s\n", sc->sc_dev.dv_xname, 462 p2[12].String.Pointer, p2[11].String.Pointer, 463 p2[9].String.Pointer, p2[10].String.Pointer); 464 465 rv = AE_OK; 466 467 out: 468 AcpiOsFree(buf.Pointer); 469 return rv; 470 } 471 472 /* 473 * acpibat_get_status: 474 * 475 * Get, and possibly display, the current battery line status. 476 */ 477 static ACPI_STATUS 478 acpibat_get_status(struct acpibat_softc *sc) 479 { 480 int flags, status, s; 481 ACPI_OBJECT *p1, *p2; 482 ACPI_STATUS rv; 483 ACPI_BUFFER buf; 484 485 rv = acpi_eval_struct(sc->sc_node->ad_handle, "_BST", &buf); 486 if (ACPI_FAILURE(rv)) { 487 printf("%s: failed to evaluate _BST: %s\n", 488 sc->sc_dev.dv_xname, AcpiFormatException(rv)); 489 return rv; 490 } 491 p1 = (ACPI_OBJECT *)buf.Pointer; 492 493 if (p1->Type != ACPI_TYPE_PACKAGE) { 494 printf("bat: expected PACKAGE, got %d\n", p1->Type); 495 rv = AE_ERROR; 496 goto out; 497 } 498 if (p1->Package.Count < 4) { 499 printf("bat: expected 4 elts, got %d\n", p1->Package.Count); 500 rv = AE_ERROR; 501 goto out; 502 } 503 p2 = p1->Package.Elements; 504 505 ABAT_LOCK(sc, s); 506 status = p2[0].Integer.Value; 507 sc->sc_data[ACPIBAT_CHARGERATE].validflags &= ~ENVSYS_FCURVALID; 508 sc->sc_data[ACPIBAT_DISCHARGERATE].validflags &= ~ENVSYS_FCURVALID; 509 if (p2[1].Integer.Value != -1) { 510 if (status & ACPIBAT_ST_CHARGING) { 511 sc->sc_data[ACPIBAT_CHARGERATE].cur.data_s = p2[1].Integer.Value * 1000; 512 sc->sc_data[ACPIBAT_CHARGERATE].validflags |= ENVSYS_FCURVALID; 513 } else if (status & ACPIBAT_ST_DISCHARGING) { 514 sc->sc_data[ACPIBAT_DISCHARGERATE].cur.data_s = p2[1].Integer.Value * 1000; 515 sc->sc_data[ACPIBAT_DISCHARGERATE].validflags |= ENVSYS_FCURVALID; 516 } 517 } 518 sc->sc_data[ACPIBAT_CAPACITY].cur.data_s = p2[2].Integer.Value * 1000; 519 sc->sc_data[ACPIBAT_CAPACITY].validflags |= ENVSYS_FCURVALID | ENVSYS_FFRACVALID; 520 sc->sc_data[ACPIBAT_VOLTAGE].cur.data_s = p2[3].Integer.Value * 1000; 521 sc->sc_data[ACPIBAT_VOLTAGE].validflags |= ENVSYS_FCURVALID; 522 flags = 0; 523 if (sc->sc_data[ACPIBAT_CAPACITY].cur.data_s < 524 sc->sc_data[ACPIBAT_WCAPACITY].cur.data_s) 525 flags |= ENVSYS_WARN_UNDER; 526 if (status & ACPIBAT_ST_CRITICAL) 527 flags |= ENVSYS_WARN_CRITUNDER; 528 sc->sc_data[ACPIBAT_CAPACITY].warnflags = flags; 529 sc->sc_data[ACPIBAT_CHARGING].cur.data_s = 530 ((status & ACPIBAT_ST_CHARGING) != 0); 531 sc->sc_data[ACPIBAT_CHARGING].validflags |= ENVSYS_FCURVALID; 532 sc->sc_data[ACPIBAT_DISCHARGING].cur.data_s = 533 ((status & ACPIBAT_ST_DISCHARGING) != 0); 534 sc->sc_data[ACPIBAT_DISCHARGING].validflags |= ENVSYS_FCURVALID; 535 sc->sc_available = ABAT_ALV_STAT; 536 ABAT_UNLOCK(sc, s); 537 538 rv = AE_OK; 539 540 out: 541 AcpiOsFree(buf.Pointer); 542 return rv; 543 } 544 545 #define SCALE(x) ((x)/1000000), (((x)%1000000)/1000) 546 #define CAPUNITS(sc) (ABAT_ISSET((sc), ABAT_F_PWRUNIT_MA)?"Ah":"Wh") 547 #define RATEUNITS(sc) (ABAT_ISSET((sc), ABAT_F_PWRUNIT_MA)?"A":"W") 548 static void 549 acpibat_print_info(struct acpibat_softc *sc) 550 { 551 const char *tech; 552 553 if (sc->sc_data[ACPIBAT_TECHNOLOGY].cur.data_s) 554 tech = "secondary"; 555 else 556 tech = "primary"; 557 558 printf("%s: %s battery, Design %d.%03d%s, Last full %d.%03d%s " 559 "Warn %d.%03d%s Low %d.%03d%s\n", 560 sc->sc_dev.dv_xname, tech, 561 SCALE(sc->sc_data[ACPIBAT_DCAPACITY].cur.data_s), CAPUNITS(sc), 562 SCALE(sc->sc_data[ACPIBAT_LFCCAPACITY].cur.data_s),CAPUNITS(sc), 563 SCALE(sc->sc_data[ACPIBAT_WCAPACITY].cur.data_s), CAPUNITS(sc), 564 SCALE(sc->sc_data[ACPIBAT_LCAPACITY].cur.data_s), CAPUNITS(sc)); 565 } 566 567 static void 568 acpibat_print_stat(struct acpibat_softc *sc) 569 { 570 const char *capstat, *chargestat; 571 int percent, denom; 572 573 percent = 0; 574 575 if (sc->sc_data[ACPIBAT_CAPACITY].warnflags&ENVSYS_WARN_CRITUNDER) 576 capstat = "CRITICAL "; 577 else if (sc->sc_data[ACPIBAT_CAPACITY].warnflags&ENVSYS_WARN_UNDER) 578 capstat = "UNDER "; 579 else 580 capstat = ""; 581 if (sc->sc_data[ACPIBAT_CHARGING].cur.data_s) 582 chargestat = "charging"; 583 else if (sc->sc_data[ACPIBAT_DISCHARGING].cur.data_s) 584 chargestat = "discharging"; 585 else 586 chargestat = "idling"; 587 denom = sc->sc_data[ACPIBAT_DCAPACITY].cur.data_s / 100; 588 if (denom > 0) 589 percent = (sc->sc_data[ACPIBAT_CAPACITY].cur.data_s) / denom; 590 printf("%s: %s%s: %d.%03dV cap %d.%03d%s (%d%%) rate %d.%03d%s\n", 591 sc->sc_dev.dv_xname, 592 capstat, chargestat, 593 SCALE(sc->sc_data[ACPIBAT_VOLTAGE].cur.data_s), 594 SCALE(sc->sc_data[ACPIBAT_CAPACITY].cur.data_s), CAPUNITS(sc), 595 percent, 596 SCALE(sc->sc_data[ACPIBAT_CHARGING].cur.data_s ? 597 sc->sc_data[ACPIBAT_CHARGERATE].cur.data_s : 598 sc->sc_data[ACPIBAT_DISCHARGING].cur.data_s ? 599 sc->sc_data[ACPIBAT_DISCHARGERATE].cur.data_s : 0), 600 RATEUNITS(sc)); 601 } 602 603 static void 604 acpibat_update(void *arg) 605 { 606 struct acpibat_softc *sc = arg; 607 608 if (sc->sc_available < ABAT_ALV_INFO) { 609 /* current information is invalid */ 610 #if 0 611 /* 612 * XXX: The driver sometimes unaware that the battery exist. 613 * (i.e. just after the boot or resuming) 614 * Thus, the driver should always check it here. 615 */ 616 if (sc->sc_available < ABAT_ALV_PRESENCE) 617 #endif 618 /* presence is invalid */ 619 if (acpibat_battery_present(sc)<0) { 620 /* error */ 621 printf("%s: cannot get battery presence.\n", 622 sc->sc_dev.dv_xname); 623 return; 624 } 625 if (ABAT_ISSET(sc, ABAT_F_PRESENT)) { 626 /* the battery is present. */ 627 if (ABAT_ISSET(sc, ABAT_F_VERBOSE)) 628 printf("%s: battery is present.\n", 629 sc->sc_dev.dv_xname); 630 if (ACPI_FAILURE(acpibat_get_info(sc))) 631 return; 632 if (ABAT_ISSET(sc, ABAT_F_VERBOSE)) 633 acpibat_print_info(sc); 634 } else { 635 /* the battery is not present. */ 636 if (ABAT_ISSET(sc, ABAT_F_VERBOSE)) 637 printf("%s: battery is not present.\n", 638 sc->sc_dev.dv_xname); 639 return; 640 } 641 } else { 642 /* current information is valid */ 643 if (!ABAT_ISSET(sc, ABAT_F_PRESENT)) { 644 /* the battery is not present. */ 645 return; 646 } 647 } 648 649 if (ACPI_FAILURE(acpibat_get_status(sc))) 650 return; 651 652 if (ABAT_ISSET(sc, ABAT_F_VERBOSE)) 653 acpibat_print_stat(sc); 654 } 655 656 /* 657 * acpibat_notify_handler: 658 * 659 * Callback from ACPI interrupt handler to notify us of an event. 660 */ 661 static void 662 acpibat_notify_handler(ACPI_HANDLE handle, UINT32 notify, void *context) 663 { 664 struct acpibat_softc *sc = context; 665 int rv, s; 666 667 #ifdef ACPI_BAT_DEBUG 668 printf("%s: received notify message: 0x%x\n", 669 sc->sc_dev.dv_xname, notify); 670 #endif 671 672 switch (notify) { 673 case ACPI_NOTIFY_BusCheck: 674 break; 675 676 case ACPI_NOTIFY_DeviceCheck: 677 case ACPI_NOTIFY_BatteryInformationChanged: 678 ABAT_LOCK(sc, s); 679 acpibat_clear_presence(sc); 680 ABAT_UNLOCK(sc, s); 681 rv = AcpiOsQueueForExecution(OSD_PRIORITY_LO, 682 acpibat_update, sc); 683 if (ACPI_FAILURE(rv)) 684 printf("%s: unable to queue status check: %s\n", 685 sc->sc_dev.dv_xname, AcpiFormatException(rv)); 686 break; 687 688 case ACPI_NOTIFY_BatteryStatusChanged: 689 ABAT_LOCK(sc, s); 690 acpibat_clear_stat(sc); 691 ABAT_UNLOCK(sc, s); 692 rv = AcpiOsQueueForExecution(OSD_PRIORITY_LO, 693 acpibat_update, sc); 694 if (ACPI_FAILURE(rv)) 695 printf("%s: unable to queue status check: %s\n", 696 sc->sc_dev.dv_xname, AcpiFormatException(rv)); 697 break; 698 699 default: 700 printf("%s: received unknown notify message: 0x%x\n", 701 sc->sc_dev.dv_xname, notify); 702 } 703 } 704 705 static void 706 acpibat_init_envsys(struct acpibat_softc *sc) 707 { 708 int capunit, rateunit; 709 710 #if 0 711 if (sc->sc_flags & ABAT_F_PWRUNIT_MA) { 712 #endif 713 /* XXX */ 714 sc->sc_sysmon.sme_ranges = acpibat_range_amp; 715 capunit = ENVSYS_SAMPHOUR; 716 rateunit = ENVSYS_SAMPS; 717 #if 0 718 } else { 719 sc->sc_sysmon.sme_ranges = acpibat_range_watt; 720 capunit = ENVSYS_SWATTHOUR; 721 rateunit = ENVSYS_SWATTS; 722 } 723 #endif 724 725 #define INITDATA(index, unit, string) \ 726 sc->sc_data[index].sensor = index; \ 727 sc->sc_data[index].units = unit; \ 728 sc->sc_data[index].validflags = ENVSYS_FVALID; \ 729 sc->sc_data[index].warnflags = 0; \ 730 sc->sc_info[index].sensor = index; \ 731 sc->sc_info[index].units = unit; \ 732 sc->sc_info[index].validflags = ENVSYS_FVALID; \ 733 snprintf(sc->sc_info[index].desc, sizeof(sc->sc_info->desc), \ 734 "%s %s", sc->sc_dev.dv_xname, string); \ 735 736 INITDATA(ACPIBAT_PRESENT, ENVSYS_INDICATOR, "present"); 737 INITDATA(ACPIBAT_DCAPACITY, capunit, "design cap"); 738 INITDATA(ACPIBAT_LFCCAPACITY, capunit, "last full cap"); 739 INITDATA(ACPIBAT_TECHNOLOGY, ENVSYS_INTEGER, "technology"); 740 INITDATA(ACPIBAT_DVOLTAGE, ENVSYS_SVOLTS_DC, "design voltage"); 741 INITDATA(ACPIBAT_WCAPACITY, capunit, "warn cap"); 742 INITDATA(ACPIBAT_LCAPACITY, capunit, "low cap"); 743 INITDATA(ACPIBAT_VOLTAGE, ENVSYS_SVOLTS_DC, "voltage"); 744 INITDATA(ACPIBAT_CHARGERATE, rateunit, "charge rate"); 745 INITDATA(ACPIBAT_DISCHARGERATE, rateunit, "discharge rate"); 746 INITDATA(ACPIBAT_CAPACITY, capunit, "charge"); 747 INITDATA(ACPIBAT_CHARGING, ENVSYS_INDICATOR, "charging"); 748 INITDATA(ACPIBAT_DISCHARGING, ENVSYS_INDICATOR, "discharging"); 749 750 #undef INITDATA 751 752 sc->sc_sysmon.sme_sensor_info = sc->sc_info; 753 sc->sc_sysmon.sme_sensor_data = sc->sc_data; 754 sc->sc_sysmon.sme_cookie = sc; 755 sc->sc_sysmon.sme_gtredata = acpibat_gtredata; 756 sc->sc_sysmon.sme_streinfo = acpibat_streinfo; 757 sc->sc_sysmon.sme_nsensors = ACPIBAT_NSENSORS; 758 sc->sc_sysmon.sme_envsys_version = 1000; 759 760 sc->sc_updateinterval.tv_sec = 1; 761 sc->sc_updateinterval.tv_usec = 0; 762 763 if (sysmon_envsys_register(&sc->sc_sysmon)) 764 printf("%s: unable to register with sysmon\n", 765 sc->sc_dev.dv_xname); 766 } 767 768 static int 769 acpibat_gtredata(struct sysmon_envsys *sme, struct envsys_tre_data *tred) 770 { 771 struct acpibat_softc *sc = sme->sme_cookie; 772 773 if (ratecheck(&sc->sc_lastupdate, &sc->sc_updateinterval)) 774 acpibat_update(sc); 775 776 /* XXX locking */ 777 *tred = sc->sc_data[tred->sensor]; 778 /* XXX locking */ 779 780 return 0; 781 } 782 783 static int 784 acpibat_streinfo(struct sysmon_envsys *sme, struct envsys_basic_info *binfo) 785 { 786 787 /* XXX Not implemented */ 788 binfo->validflags = 0; 789 790 return 0; 791 } 792