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