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