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