1 /* $NetBSD: acpi_bat.c,v 1.45 2006/11/16 01:32:47 christos 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.45 2006/11/16 01:32:47 christos 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, 244 void *aux) 245 { 246 struct acpi_attach_args *aa = aux; 247 248 if (aa->aa_node->ad_type != ACPI_TYPE_DEVICE) 249 return 0; 250 251 return acpi_match_hid(aa->aa_node->ad_devinfo, bat_hid); 252 } 253 254 /* 255 * acpibat_attach: 256 * 257 * Autoconfiguration `attach' routine. 258 */ 259 static void 260 acpibat_attach(struct device *parent, struct device *self, void *aux) 261 { 262 struct acpibat_softc *sc = (void *) self; 263 struct acpi_attach_args *aa = aux; 264 ACPI_STATUS rv; 265 266 aprint_naive(": ACPI Battery (Control Method)\n"); 267 aprint_normal(": ACPI Battery (Control Method)\n"); 268 269 sc->sc_node = aa->aa_node; 270 simple_lock_init(&sc->sc_lock); 271 272 rv = AcpiInstallNotifyHandler(sc->sc_node->ad_handle, 273 ACPI_DEVICE_NOTIFY, 274 acpibat_notify_handler, sc); 275 if (ACPI_FAILURE(rv)) { 276 aprint_error("%s: unable to register DEVICE NOTIFY handler: %s\n", 277 sc->sc_dev.dv_xname, AcpiFormatException(rv)); 278 return; 279 } 280 281 /* XXX See acpibat_notify_handler() */ 282 rv = AcpiInstallNotifyHandler(sc->sc_node->ad_handle, 283 ACPI_SYSTEM_NOTIFY, 284 acpibat_notify_handler, sc); 285 if (ACPI_FAILURE(rv)) { 286 aprint_error("%s: unable to register SYSTEM NOTIFY handler: %s\n", 287 sc->sc_dev.dv_xname, AcpiFormatException(rv)); 288 return; 289 } 290 291 #ifdef ACPI_BAT_DEBUG 292 ABAT_SET(sc, ABAT_F_VERBOSE); 293 #endif 294 295 acpibat_init_envsys(sc); 296 } 297 298 /* 299 * clear informations 300 */ 301 302 static void 303 acpibat_clear_presence(struct acpibat_softc *sc) 304 { 305 306 ABAT_ASSERT_LOCKED(sc); 307 308 acpibat_clear_info(sc); 309 sc->sc_available = ABAT_ALV_NONE; 310 ABAT_CLEAR(sc, ABAT_F_PRESENT); 311 } 312 313 static void 314 acpibat_clear_info(struct acpibat_softc *sc) 315 { 316 317 ABAT_ASSERT_LOCKED(sc); 318 319 acpibat_clear_stat(sc); 320 if (sc->sc_available>ABAT_ALV_PRESENCE) 321 sc->sc_available = ABAT_ALV_PRESENCE; 322 sc->sc_data[ACPIBAT_DCAPACITY].validflags &= ~ENVSYS_FCURVALID; 323 sc->sc_data[ACPIBAT_LFCCAPACITY].validflags &= ~ENVSYS_FCURVALID; 324 sc->sc_data[ACPIBAT_CAPACITY].validflags &= ~ENVSYS_FMAXVALID; 325 sc->sc_data[ACPIBAT_TECHNOLOGY].validflags &= ~ENVSYS_FCURVALID; 326 sc->sc_data[ACPIBAT_DVOLTAGE].validflags &= ~ENVSYS_FCURVALID; 327 sc->sc_data[ACPIBAT_WCAPACITY].validflags &= ~(ENVSYS_FCURVALID | ENVSYS_FMAXVALID | ENVSYS_FFRACVALID); 328 sc->sc_data[ACPIBAT_LCAPACITY].validflags &= ~(ENVSYS_FCURVALID | ENVSYS_FMAXVALID | ENVSYS_FFRACVALID); 329 } 330 331 static void 332 acpibat_clear_stat(struct acpibat_softc *sc) 333 { 334 335 ABAT_ASSERT_LOCKED(sc); 336 337 if (sc->sc_available>ABAT_ALV_INFO) 338 sc->sc_available = ABAT_ALV_INFO; 339 sc->sc_data[ACPIBAT_CHARGERATE].validflags &= ~ENVSYS_FCURVALID; 340 sc->sc_data[ACPIBAT_DISCHARGERATE].validflags &= ~ENVSYS_FCURVALID; 341 sc->sc_data[ACPIBAT_CAPACITY].validflags &= ~(ENVSYS_FCURVALID | ENVSYS_FFRACVALID); 342 sc->sc_data[ACPIBAT_CAPACITY].warnflags = 0; 343 sc->sc_data[ACPIBAT_VOLTAGE].validflags &= ~ENVSYS_FCURVALID; 344 sc->sc_data[ACPIBAT_CHARGING].validflags &= ~ENVSYS_FCURVALID; 345 sc->sc_data[ACPIBAT_DISCHARGING].validflags &= ~ENVSYS_FCURVALID; 346 } 347 348 349 /* 350 * returns 0 for no battery, 1 for present, and -1 on error 351 */ 352 static int 353 acpibat_battery_present(struct acpibat_softc *sc) 354 { 355 u_int32_t sta; 356 int s; 357 ACPI_INTEGER val; 358 ACPI_STATUS rv; 359 360 rv = acpi_eval_integer(sc->sc_node->ad_handle, "_STA", &val); 361 if (ACPI_FAILURE(rv)) { 362 printf("%s: failed to evaluate _STA: %s\n", 363 sc->sc_dev.dv_xname, AcpiFormatException(rv)); 364 return -1; 365 } 366 367 sta = (u_int32_t)val; 368 369 ABAT_LOCK(sc, s); 370 sc->sc_available = ABAT_ALV_PRESENCE; 371 if (sta & ACPIBAT_STA_PRESENT) { 372 ABAT_SET(sc, ABAT_F_PRESENT); 373 sc->sc_data[ACPIBAT_PRESENT].cur.data_s = 1; 374 } else 375 sc->sc_data[ACPIBAT_PRESENT].cur.data_s = 0; 376 sc->sc_data[ACPIBAT_PRESENT].validflags |= ENVSYS_FCURVALID; 377 ABAT_UNLOCK(sc, s); 378 379 return (sta & ACPIBAT_STA_PRESENT) ? 1 : 0; 380 } 381 382 /* 383 * acpibat_get_info 384 * 385 * Get, and possibly display, the battery info. 386 */ 387 388 static ACPI_STATUS 389 acpibat_get_info(struct acpibat_softc *sc) 390 { 391 ACPI_OBJECT *p1, *p2; 392 ACPI_STATUS rv; 393 ACPI_BUFFER buf; 394 int capunit, rateunit, s; 395 396 rv = acpi_eval_struct(sc->sc_node->ad_handle, "_BIF", &buf); 397 if (ACPI_FAILURE(rv)) { 398 printf("%s: failed to evaluate _BIF: %s\n", 399 sc->sc_dev.dv_xname, AcpiFormatException(rv)); 400 return rv; 401 } 402 p1 = (ACPI_OBJECT *)buf.Pointer; 403 404 if (p1->Type != ACPI_TYPE_PACKAGE) { 405 printf("%s: expected PACKAGE, got %d\n", sc->sc_dev.dv_xname, 406 p1->Type); 407 goto out; 408 } 409 if (p1->Package.Count < 13) { 410 printf("%s: expected 13 elts, got %d\n", 411 sc->sc_dev.dv_xname, p1->Package.Count); 412 goto out; 413 } 414 p2 = p1->Package.Elements; 415 416 ABAT_LOCK(sc, s); 417 if ((p2[0].Integer.Value & ACPIBAT_PWRUNIT_MA) != 0) { 418 ABAT_SET(sc, ABAT_F_PWRUNIT_MA); 419 sc->sc_sysmon.sme_ranges = acpibat_range_amp; 420 capunit = ENVSYS_SAMPHOUR; 421 rateunit = ENVSYS_SAMPS; 422 } else { 423 ABAT_CLEAR(sc, ABAT_F_PWRUNIT_MA); 424 sc->sc_sysmon.sme_ranges = acpibat_range_watt; 425 capunit = ENVSYS_SWATTHOUR; 426 rateunit = ENVSYS_SWATTS; 427 } 428 429 #define INITDATA(index, unit) \ 430 sc->sc_data[index].units = unit; \ 431 sc->sc_info[index].units = unit; 432 433 INITDATA(ACPIBAT_DCAPACITY, capunit); 434 INITDATA(ACPIBAT_LFCCAPACITY, capunit); 435 INITDATA(ACPIBAT_WCAPACITY, capunit); 436 INITDATA(ACPIBAT_LCAPACITY, capunit); 437 INITDATA(ACPIBAT_CHARGERATE, rateunit); 438 INITDATA(ACPIBAT_DISCHARGERATE, rateunit); 439 INITDATA(ACPIBAT_CAPACITY, capunit); 440 441 #undef INITDATA 442 443 sc->sc_data[ACPIBAT_DCAPACITY].cur.data_s = p2[1].Integer.Value * 1000; 444 sc->sc_data[ACPIBAT_DCAPACITY].validflags |= ENVSYS_FCURVALID; 445 sc->sc_data[ACPIBAT_LFCCAPACITY].cur.data_s = p2[2].Integer.Value * 1000; 446 sc->sc_data[ACPIBAT_LFCCAPACITY].validflags |= ENVSYS_FCURVALID; 447 sc->sc_data[ACPIBAT_CAPACITY].max.data_s = p2[2].Integer.Value * 1000; 448 sc->sc_data[ACPIBAT_CAPACITY].validflags |= ENVSYS_FMAXVALID; 449 sc->sc_data[ACPIBAT_TECHNOLOGY].cur.data_s = p2[3].Integer.Value; 450 sc->sc_data[ACPIBAT_TECHNOLOGY].validflags |= ENVSYS_FCURVALID; 451 sc->sc_data[ACPIBAT_DVOLTAGE].cur.data_s = p2[4].Integer.Value * 1000; 452 sc->sc_data[ACPIBAT_DVOLTAGE].validflags |= ENVSYS_FCURVALID; 453 sc->sc_data[ACPIBAT_WCAPACITY].cur.data_s = p2[5].Integer.Value * 1000; 454 sc->sc_data[ACPIBAT_WCAPACITY].max.data_s = p2[2].Integer.Value * 1000; 455 sc->sc_data[ACPIBAT_WCAPACITY].validflags |= ENVSYS_FCURVALID | ENVSYS_FMAXVALID | ENVSYS_FFRACVALID; 456 sc->sc_data[ACPIBAT_LCAPACITY].cur.data_s = p2[6].Integer.Value * 1000; 457 sc->sc_data[ACPIBAT_LCAPACITY].max.data_s = p2[2].Integer.Value * 1000; 458 sc->sc_data[ACPIBAT_LCAPACITY].validflags |= ENVSYS_FCURVALID | ENVSYS_FMAXVALID | ENVSYS_FFRACVALID; 459 sc->sc_available = ABAT_ALV_INFO; 460 ABAT_UNLOCK(sc, s); 461 462 printf("%s: battery info: %s, %s, %s, %s\n", sc->sc_dev.dv_xname, 463 p2[12].String.Pointer, p2[11].String.Pointer, 464 p2[9].String.Pointer, p2[10].String.Pointer); 465 466 rv = AE_OK; 467 468 out: 469 AcpiOsFree(buf.Pointer); 470 return rv; 471 } 472 473 /* 474 * acpibat_get_status: 475 * 476 * Get, and possibly display, the current battery line status. 477 */ 478 static ACPI_STATUS 479 acpibat_get_status(struct acpibat_softc *sc) 480 { 481 int flags, status, s; 482 ACPI_OBJECT *p1, *p2; 483 ACPI_STATUS rv; 484 ACPI_BUFFER buf; 485 486 rv = acpi_eval_struct(sc->sc_node->ad_handle, "_BST", &buf); 487 if (ACPI_FAILURE(rv)) { 488 printf("%s: failed to evaluate _BST: %s\n", 489 sc->sc_dev.dv_xname, AcpiFormatException(rv)); 490 return rv; 491 } 492 p1 = (ACPI_OBJECT *)buf.Pointer; 493 494 if (p1->Type != ACPI_TYPE_PACKAGE) { 495 printf("bat: expected PACKAGE, got %d\n", p1->Type); 496 rv = AE_ERROR; 497 goto out; 498 } 499 if (p1->Package.Count < 4) { 500 printf("bat: expected 4 elts, got %d\n", p1->Package.Count); 501 rv = AE_ERROR; 502 goto out; 503 } 504 p2 = p1->Package.Elements; 505 506 ABAT_LOCK(sc, s); 507 status = p2[0].Integer.Value; 508 sc->sc_data[ACPIBAT_CHARGERATE].validflags &= ~ENVSYS_FCURVALID; 509 sc->sc_data[ACPIBAT_DISCHARGERATE].validflags &= ~ENVSYS_FCURVALID; 510 if (p2[1].Integer.Value != -1) { 511 if (status & ACPIBAT_ST_CHARGING) { 512 sc->sc_data[ACPIBAT_CHARGERATE].cur.data_s = p2[1].Integer.Value * 1000; 513 sc->sc_data[ACPIBAT_CHARGERATE].validflags |= ENVSYS_FCURVALID; 514 } else if (status & ACPIBAT_ST_DISCHARGING) { 515 sc->sc_data[ACPIBAT_DISCHARGERATE].cur.data_s = p2[1].Integer.Value * 1000; 516 sc->sc_data[ACPIBAT_DISCHARGERATE].validflags |= ENVSYS_FCURVALID; 517 } 518 } 519 sc->sc_data[ACPIBAT_CAPACITY].cur.data_s = p2[2].Integer.Value * 1000; 520 sc->sc_data[ACPIBAT_CAPACITY].validflags |= ENVSYS_FCURVALID | ENVSYS_FFRACVALID; 521 sc->sc_data[ACPIBAT_VOLTAGE].cur.data_s = p2[3].Integer.Value * 1000; 522 sc->sc_data[ACPIBAT_VOLTAGE].validflags |= ENVSYS_FCURVALID; 523 flags = 0; 524 if (sc->sc_data[ACPIBAT_CAPACITY].cur.data_s < 525 sc->sc_data[ACPIBAT_WCAPACITY].cur.data_s) 526 flags |= ENVSYS_WARN_UNDER; 527 if (status & ACPIBAT_ST_CRITICAL) 528 flags |= ENVSYS_WARN_CRITUNDER; 529 sc->sc_data[ACPIBAT_CAPACITY].warnflags = flags; 530 sc->sc_data[ACPIBAT_CHARGING].cur.data_s = 531 ((status & ACPIBAT_ST_CHARGING) != 0); 532 sc->sc_data[ACPIBAT_CHARGING].validflags |= ENVSYS_FCURVALID; 533 sc->sc_data[ACPIBAT_DISCHARGING].cur.data_s = 534 ((status & ACPIBAT_ST_DISCHARGING) != 0); 535 sc->sc_data[ACPIBAT_DISCHARGING].validflags |= ENVSYS_FCURVALID; 536 sc->sc_available = ABAT_ALV_STAT; 537 ABAT_UNLOCK(sc, s); 538 539 rv = AE_OK; 540 541 out: 542 AcpiOsFree(buf.Pointer); 543 return rv; 544 } 545 546 #define SCALE(x) ((x)/1000000), (((x)%1000000)/1000) 547 #define CAPUNITS(sc) (ABAT_ISSET((sc), ABAT_F_PWRUNIT_MA)?"Ah":"Wh") 548 #define RATEUNITS(sc) (ABAT_ISSET((sc), ABAT_F_PWRUNIT_MA)?"A":"W") 549 static void 550 acpibat_print_info(struct acpibat_softc *sc) 551 { 552 const char *tech; 553 554 if (sc->sc_data[ACPIBAT_TECHNOLOGY].cur.data_s) 555 tech = "secondary"; 556 else 557 tech = "primary"; 558 559 printf("%s: %s battery, Design %d.%03d%s, Last full %d.%03d%s " 560 "Warn %d.%03d%s Low %d.%03d%s\n", 561 sc->sc_dev.dv_xname, tech, 562 SCALE(sc->sc_data[ACPIBAT_DCAPACITY].cur.data_s), CAPUNITS(sc), 563 SCALE(sc->sc_data[ACPIBAT_LFCCAPACITY].cur.data_s),CAPUNITS(sc), 564 SCALE(sc->sc_data[ACPIBAT_WCAPACITY].cur.data_s), CAPUNITS(sc), 565 SCALE(sc->sc_data[ACPIBAT_LCAPACITY].cur.data_s), CAPUNITS(sc)); 566 } 567 568 static void 569 acpibat_print_stat(struct acpibat_softc *sc) 570 { 571 const char *capstat, *chargestat; 572 int percent, denom; 573 574 percent = 0; 575 576 if (sc->sc_data[ACPIBAT_CAPACITY].warnflags&ENVSYS_WARN_CRITUNDER) 577 capstat = "CRITICAL "; 578 else if (sc->sc_data[ACPIBAT_CAPACITY].warnflags&ENVSYS_WARN_UNDER) 579 capstat = "UNDER "; 580 else 581 capstat = ""; 582 if (sc->sc_data[ACPIBAT_CHARGING].cur.data_s) 583 chargestat = "charging"; 584 else if (sc->sc_data[ACPIBAT_DISCHARGING].cur.data_s) 585 chargestat = "discharging"; 586 else 587 chargestat = "idling"; 588 denom = sc->sc_data[ACPIBAT_DCAPACITY].cur.data_s / 100; 589 if (denom > 0) 590 percent = (sc->sc_data[ACPIBAT_CAPACITY].cur.data_s) / denom; 591 printf("%s: %s%s: %d.%03dV cap %d.%03d%s (%d%%) rate %d.%03d%s\n", 592 sc->sc_dev.dv_xname, 593 capstat, chargestat, 594 SCALE(sc->sc_data[ACPIBAT_VOLTAGE].cur.data_s), 595 SCALE(sc->sc_data[ACPIBAT_CAPACITY].cur.data_s), CAPUNITS(sc), 596 percent, 597 SCALE(sc->sc_data[ACPIBAT_CHARGING].cur.data_s ? 598 sc->sc_data[ACPIBAT_CHARGERATE].cur.data_s : 599 sc->sc_data[ACPIBAT_DISCHARGING].cur.data_s ? 600 sc->sc_data[ACPIBAT_DISCHARGERATE].cur.data_s : 0), 601 RATEUNITS(sc)); 602 } 603 604 static void 605 acpibat_update(void *arg) 606 { 607 struct acpibat_softc *sc = arg; 608 609 if (sc->sc_available < ABAT_ALV_INFO) { 610 /* current information is invalid */ 611 #if 0 612 /* 613 * XXX: The driver sometimes unaware that the battery exist. 614 * (i.e. just after the boot or resuming) 615 * Thus, the driver should always check it here. 616 */ 617 if (sc->sc_available < ABAT_ALV_PRESENCE) 618 #endif 619 /* presence is invalid */ 620 if (acpibat_battery_present(sc)<0) { 621 /* error */ 622 printf("%s: cannot get battery presence.\n", 623 sc->sc_dev.dv_xname); 624 return; 625 } 626 if (ABAT_ISSET(sc, ABAT_F_PRESENT)) { 627 /* the battery is present. */ 628 if (ABAT_ISSET(sc, ABAT_F_VERBOSE)) 629 printf("%s: battery is present.\n", 630 sc->sc_dev.dv_xname); 631 if (ACPI_FAILURE(acpibat_get_info(sc))) 632 return; 633 if (ABAT_ISSET(sc, ABAT_F_VERBOSE)) 634 acpibat_print_info(sc); 635 } else { 636 /* the battery is not present. */ 637 if (ABAT_ISSET(sc, ABAT_F_VERBOSE)) 638 printf("%s: battery is not present.\n", 639 sc->sc_dev.dv_xname); 640 return; 641 } 642 } else { 643 /* current information is valid */ 644 if (!ABAT_ISSET(sc, ABAT_F_PRESENT)) { 645 /* the battery is not present. */ 646 return; 647 } 648 } 649 650 if (ACPI_FAILURE(acpibat_get_status(sc))) 651 return; 652 653 if (ABAT_ISSET(sc, ABAT_F_VERBOSE)) 654 acpibat_print_stat(sc); 655 } 656 657 /* 658 * acpibat_notify_handler: 659 * 660 * Callback from ACPI interrupt handler to notify us of an event. 661 */ 662 static void 663 acpibat_notify_handler(ACPI_HANDLE handle, UINT32 notify, 664 void *context) 665 { 666 struct acpibat_softc *sc = context; 667 int rv, s; 668 669 #ifdef ACPI_BAT_DEBUG 670 printf("%s: received notify message: 0x%x\n", 671 sc->sc_dev.dv_xname, notify); 672 #endif 673 674 switch (notify) { 675 case ACPI_NOTIFY_BusCheck: 676 break; 677 678 case ACPI_NOTIFY_DeviceCheck: 679 case ACPI_NOTIFY_BatteryInformationChanged: 680 ABAT_LOCK(sc, s); 681 acpibat_clear_presence(sc); 682 ABAT_UNLOCK(sc, s); 683 rv = AcpiOsQueueForExecution(OSD_PRIORITY_LO, 684 acpibat_update, sc); 685 if (ACPI_FAILURE(rv)) 686 printf("%s: unable to queue status check: %s\n", 687 sc->sc_dev.dv_xname, AcpiFormatException(rv)); 688 break; 689 690 case ACPI_NOTIFY_BatteryStatusChanged: 691 ABAT_LOCK(sc, s); 692 acpibat_clear_stat(sc); 693 ABAT_UNLOCK(sc, s); 694 rv = AcpiOsQueueForExecution(OSD_PRIORITY_LO, 695 acpibat_update, sc); 696 if (ACPI_FAILURE(rv)) 697 printf("%s: unable to queue status check: %s\n", 698 sc->sc_dev.dv_xname, AcpiFormatException(rv)); 699 break; 700 701 default: 702 printf("%s: received unknown notify message: 0x%x\n", 703 sc->sc_dev.dv_xname, notify); 704 } 705 } 706 707 static void 708 acpibat_init_envsys(struct acpibat_softc *sc) 709 { 710 int capunit, rateunit; 711 712 #if 0 713 if (sc->sc_flags & ABAT_F_PWRUNIT_MA) { 714 #endif 715 /* XXX */ 716 sc->sc_sysmon.sme_ranges = acpibat_range_amp; 717 capunit = ENVSYS_SAMPHOUR; 718 rateunit = ENVSYS_SAMPS; 719 #if 0 720 } else { 721 sc->sc_sysmon.sme_ranges = acpibat_range_watt; 722 capunit = ENVSYS_SWATTHOUR; 723 rateunit = ENVSYS_SWATTS; 724 } 725 #endif 726 727 #define INITDATA(index, unit, string) \ 728 sc->sc_data[index].sensor = index; \ 729 sc->sc_data[index].units = unit; \ 730 sc->sc_data[index].validflags = ENVSYS_FVALID; \ 731 sc->sc_data[index].warnflags = 0; \ 732 sc->sc_info[index].sensor = index; \ 733 sc->sc_info[index].units = unit; \ 734 sc->sc_info[index].validflags = ENVSYS_FVALID; \ 735 snprintf(sc->sc_info[index].desc, sizeof(sc->sc_info->desc), \ 736 "%s %s", sc->sc_dev.dv_xname, string); \ 737 738 INITDATA(ACPIBAT_PRESENT, ENVSYS_INDICATOR, "present"); 739 INITDATA(ACPIBAT_DCAPACITY, capunit, "design cap"); 740 INITDATA(ACPIBAT_LFCCAPACITY, capunit, "last full cap"); 741 INITDATA(ACPIBAT_TECHNOLOGY, ENVSYS_INTEGER, "technology"); 742 INITDATA(ACPIBAT_DVOLTAGE, ENVSYS_SVOLTS_DC, "design voltage"); 743 INITDATA(ACPIBAT_WCAPACITY, capunit, "warn cap"); 744 INITDATA(ACPIBAT_LCAPACITY, capunit, "low cap"); 745 INITDATA(ACPIBAT_VOLTAGE, ENVSYS_SVOLTS_DC, "voltage"); 746 INITDATA(ACPIBAT_CHARGERATE, rateunit, "charge rate"); 747 INITDATA(ACPIBAT_DISCHARGERATE, rateunit, "discharge rate"); 748 INITDATA(ACPIBAT_CAPACITY, capunit, "charge"); 749 INITDATA(ACPIBAT_CHARGING, ENVSYS_INDICATOR, "charging"); 750 INITDATA(ACPIBAT_DISCHARGING, ENVSYS_INDICATOR, "discharging"); 751 752 #undef INITDATA 753 754 sc->sc_sysmon.sme_sensor_info = sc->sc_info; 755 sc->sc_sysmon.sme_sensor_data = sc->sc_data; 756 sc->sc_sysmon.sme_cookie = sc; 757 sc->sc_sysmon.sme_gtredata = acpibat_gtredata; 758 sc->sc_sysmon.sme_streinfo = acpibat_streinfo; 759 sc->sc_sysmon.sme_nsensors = ACPIBAT_NSENSORS; 760 sc->sc_sysmon.sme_envsys_version = 1000; 761 762 sc->sc_updateinterval.tv_sec = 1; 763 sc->sc_updateinterval.tv_usec = 0; 764 765 if (sysmon_envsys_register(&sc->sc_sysmon)) 766 printf("%s: unable to register with sysmon\n", 767 sc->sc_dev.dv_xname); 768 } 769 770 static int 771 acpibat_gtredata(struct sysmon_envsys *sme, struct envsys_tre_data *tred) 772 { 773 struct acpibat_softc *sc = sme->sme_cookie; 774 775 if (ratecheck(&sc->sc_lastupdate, &sc->sc_updateinterval)) 776 acpibat_update(sc); 777 778 /* XXX locking */ 779 *tred = sc->sc_data[tred->sensor]; 780 /* XXX locking */ 781 782 return 0; 783 } 784 785 static int 786 acpibat_streinfo(struct sysmon_envsys *sme, 787 struct envsys_basic_info *binfo) 788 { 789 790 /* XXX Not implemented */ 791 binfo->validflags = 0; 792 793 return 0; 794 } 795