1 /*- 2 * Copyright (c) 2000 Munehiro Matsuda 3 * Copyright (c) 2000 Takanori Watanabe 4 * Copyright (c) 2000 Mitsuru IWASAKI <iwasaki@FreeBSD.org> 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 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 * 28 * $FreeBSD$ 29 */ 30 31 #include "opt_acpi.h" 32 #include <sys/param.h> 33 #include <sys/kernel.h> 34 #include <sys/bus.h> 35 #include <sys/ioccom.h> 36 37 #include <machine/bus.h> 38 #include <sys/rman.h> 39 #include <sys/malloc.h> 40 41 #include "acpi.h" 42 #include <dev/acpica/acpivar.h> 43 #include <dev/acpica/acpiio.h> 44 45 MALLOC_DEFINE(M_ACPICMBAT, "acpicmbat", "ACPI control method battery data"); 46 47 /* Number of times to retry initialization before giving up. */ 48 #define ACPI_CMBAT_RETRY_MAX 6 49 50 /* Check the battery once a minute. */ 51 #define CMBAT_POLLRATE (60 * hz) 52 53 /* Hooks for the ACPI CA debugging infrastructure */ 54 #define _COMPONENT ACPI_BATTERY 55 ACPI_MODULE_NAME("BATTERY") 56 57 #define ACPI_BATTERY_BST_CHANGE 0x80 58 #define ACPI_BATTERY_BIF_CHANGE 0x81 59 60 #define PKG_GETINT(res, tmp, idx, dest, label) do { \ 61 tmp = &res->Package.Elements[idx]; \ 62 if (tmp == NULL) { \ 63 ACPI_VPRINT(dev, acpi_device_get_parent_softc(dev), \ 64 "%s: PKG_GETINT error, idx = %d\n.", __func__, idx); \ 65 goto label; \ 66 } \ 67 if (tmp->Type != ACPI_TYPE_INTEGER) \ 68 goto label; \ 69 dest = tmp->Integer.Value; \ 70 } while (0) 71 72 #define PKG_GETSTR(res, tmp, idx, dest, size, label) do { \ 73 size_t length; \ 74 length = size; \ 75 tmp = &res->Package.Elements[idx]; \ 76 if (tmp == NULL) { \ 77 ACPI_VPRINT(dev, acpi_device_get_parent_softc(dev), \ 78 "%s: PKG_GETSTR error, idx = %d\n.", __func__, idx); \ 79 goto label; \ 80 } \ 81 bzero(dest, sizeof(dest)); \ 82 switch (tmp->Type) { \ 83 case ACPI_TYPE_STRING: \ 84 if (tmp->String.Length < length) \ 85 length = tmp->String.Length; \ 86 strncpy(dest, tmp->String.Pointer, length); \ 87 break; \ 88 case ACPI_TYPE_BUFFER: \ 89 if (tmp->Buffer.Length < length) \ 90 length = tmp->Buffer.Length; \ 91 strncpy(dest, tmp->Buffer.Pointer, length); \ 92 break; \ 93 default: \ 94 goto label; \ 95 } \ 96 dest[sizeof(dest)-1] = '\0'; \ 97 } while (0) 98 99 struct acpi_cmbat_softc { 100 device_t dev; 101 102 struct acpi_bif bif; 103 struct acpi_bst bst; 104 struct timespec bif_lastupdated; 105 struct timespec bst_lastupdated; 106 int bif_updating; 107 int bst_updating; 108 109 int present; 110 int cap; 111 int min; 112 int full_charge_time; 113 int initializing; 114 }; 115 116 static struct timespec acpi_cmbat_info_lastupdated; 117 118 /* XXX: devclass_get_maxunit() don't give us the current allocated units. */ 119 static int acpi_cmbat_units = 0; 120 121 static int acpi_cmbat_info_expired(struct timespec *); 122 static void acpi_cmbat_info_updated(struct timespec *); 123 static void acpi_cmbat_get_bst(void *); 124 static void acpi_cmbat_get_bif(void *); 125 static void acpi_cmbat_notify_handler(ACPI_HANDLE, UINT32, void *); 126 static int acpi_cmbat_probe(device_t); 127 static int acpi_cmbat_attach(device_t); 128 static int acpi_cmbat_resume(device_t); 129 static int acpi_cmbat_ioctl(u_long, caddr_t, void *); 130 static int acpi_cmbat_is_bst_valid(struct acpi_bst*); 131 static int acpi_cmbat_is_bif_valid(struct acpi_bif*); 132 static int acpi_cmbat_get_total_battinfo(struct acpi_battinfo *); 133 static void acpi_cmbat_init_battery(void *); 134 135 static device_method_t acpi_cmbat_methods[] = { 136 /* Device interface */ 137 DEVMETHOD(device_probe, acpi_cmbat_probe), 138 DEVMETHOD(device_attach, acpi_cmbat_attach), 139 DEVMETHOD(device_resume, acpi_cmbat_resume), 140 141 {0, 0} 142 }; 143 144 static driver_t acpi_cmbat_driver = { 145 "acpi_cmbat", 146 acpi_cmbat_methods, 147 sizeof(struct acpi_cmbat_softc), 148 }; 149 150 static devclass_t acpi_cmbat_devclass; 151 DRIVER_MODULE(acpi_cmbat, acpi, acpi_cmbat_driver, acpi_cmbat_devclass, 0, 0); 152 153 static int 154 acpi_cmbat_info_expired(struct timespec *lastupdated) 155 { 156 struct timespec curtime; 157 158 if (lastupdated == NULL) 159 return (1); 160 if (!timespecisset(lastupdated)) 161 return (1); 162 163 getnanotime(&curtime); 164 timespecsub(&curtime, lastupdated); 165 return (curtime.tv_sec < 0 || 166 curtime.tv_sec > acpi_battery_get_info_expire()); 167 } 168 169 170 static void 171 acpi_cmbat_info_updated(struct timespec *lastupdated) 172 { 173 if (lastupdated != NULL) 174 getnanotime(lastupdated); 175 } 176 177 static void 178 acpi_cmbat_get_bst(void *context) 179 { 180 device_t dev; 181 struct acpi_cmbat_softc *sc; 182 ACPI_STATUS as; 183 ACPI_OBJECT *res, *tmp; 184 ACPI_HANDLE h; 185 ACPI_BUFFER bst_buffer; 186 187 dev = context; 188 sc = device_get_softc(dev); 189 h = acpi_get_handle(dev); 190 bst_buffer.Pointer = NULL; 191 192 if (!acpi_cmbat_info_expired(&sc->bst_lastupdated)) 193 return; 194 if (sc->bst_updating) 195 return; 196 sc->bst_updating = 1; 197 198 bst_buffer.Length = ACPI_ALLOCATE_BUFFER; 199 as = AcpiEvaluateObject(h, "_BST", NULL, &bst_buffer); 200 if (ACPI_FAILURE(as)) { 201 ACPI_VPRINT(dev, acpi_device_get_parent_softc(dev), 202 "error fetching current battery status -- %s\n", 203 AcpiFormatException(as)); 204 goto end; 205 } 206 207 res = (ACPI_OBJECT *)bst_buffer.Pointer; 208 if (res == NULL || res->Type != ACPI_TYPE_PACKAGE || 209 res->Package.Count != 4) { 210 211 ACPI_VPRINT(dev, acpi_device_get_parent_softc(dev), 212 "battery status corrupted\n"); 213 goto end; 214 } 215 216 PKG_GETINT(res, tmp, 0, sc->bst.state, end); 217 PKG_GETINT(res, tmp, 1, sc->bst.rate, end); 218 PKG_GETINT(res, tmp, 2, sc->bst.cap, end); 219 PKG_GETINT(res, tmp, 3, sc->bst.volt, end); 220 acpi_cmbat_info_updated(&sc->bst_lastupdated); 221 222 end: 223 if (bst_buffer.Pointer != NULL) 224 AcpiOsFree(bst_buffer.Pointer); 225 sc->bst_updating = 0; 226 } 227 228 static void 229 acpi_cmbat_get_bif(void *context) 230 { 231 device_t dev; 232 struct acpi_cmbat_softc *sc; 233 ACPI_STATUS as; 234 ACPI_OBJECT *res, *tmp; 235 ACPI_HANDLE h; 236 ACPI_BUFFER bif_buffer; 237 238 dev = context; 239 sc = device_get_softc(dev); 240 h = acpi_get_handle(dev); 241 bif_buffer.Pointer = NULL; 242 243 if (!acpi_cmbat_info_expired(&sc->bif_lastupdated)) 244 return; 245 if (sc->bif_updating) 246 return; 247 sc->bif_updating = 1; 248 249 bif_buffer.Length = ACPI_ALLOCATE_BUFFER; 250 as = AcpiEvaluateObject(h, "_BIF", NULL, &bif_buffer); 251 if (ACPI_FAILURE(as)) { 252 ACPI_VPRINT(dev, acpi_device_get_parent_softc(dev), 253 "error fetching current battery info -- %s\n", 254 AcpiFormatException(as)); 255 goto end; 256 } 257 258 res = (ACPI_OBJECT *)bif_buffer.Pointer; 259 if (res == NULL || res->Type != ACPI_TYPE_PACKAGE || 260 res->Package.Count != 13) { 261 262 ACPI_VPRINT(dev, acpi_device_get_parent_softc(dev), 263 "battery info corrupted\n"); 264 goto end; 265 } 266 267 PKG_GETINT(res, tmp, 0, sc->bif.units, end); 268 PKG_GETINT(res, tmp, 1, sc->bif.dcap, end); 269 PKG_GETINT(res, tmp, 2, sc->bif.lfcap, end); 270 PKG_GETINT(res, tmp, 3, sc->bif.btech, end); 271 PKG_GETINT(res, tmp, 4, sc->bif.dvol, end); 272 PKG_GETINT(res, tmp, 5, sc->bif.wcap, end); 273 PKG_GETINT(res, tmp, 6, sc->bif.lcap, end); 274 PKG_GETINT(res, tmp, 7, sc->bif.gra1, end); 275 PKG_GETINT(res, tmp, 8, sc->bif.gra2, end); 276 PKG_GETSTR(res, tmp, 9, sc->bif.model, ACPI_CMBAT_MAXSTRLEN, end); 277 PKG_GETSTR(res, tmp, 10, sc->bif.serial, ACPI_CMBAT_MAXSTRLEN, end); 278 PKG_GETSTR(res, tmp, 11, sc->bif.type, ACPI_CMBAT_MAXSTRLEN, end); 279 PKG_GETSTR(res, tmp, 12, sc->bif.oeminfo, ACPI_CMBAT_MAXSTRLEN, end); 280 acpi_cmbat_info_updated(&sc->bif_lastupdated); 281 282 end: 283 if (bif_buffer.Pointer != NULL) 284 AcpiOsFree(bif_buffer.Pointer); 285 sc->bif_updating = 0; 286 } 287 288 static void 289 acpi_cmbat_notify_handler(ACPI_HANDLE h, UINT32 notify, void *context) 290 { 291 device_t dev; 292 struct acpi_cmbat_softc *sc; 293 294 dev = (device_t)context; 295 if ((sc = device_get_softc(dev)) == NULL) 296 return; 297 298 switch (notify) { 299 case ACPI_NOTIFY_DEVICE_CHECK: 300 case ACPI_BATTERY_BST_CHANGE: 301 timespecclear(&sc->bst_lastupdated); 302 break; 303 case ACPI_NOTIFY_BUS_CHECK: 304 case ACPI_BATTERY_BIF_CHANGE: 305 timespecclear(&sc->bif_lastupdated); 306 AcpiOsQueueForExecution(OSD_PRIORITY_LO, acpi_cmbat_get_bif, dev); 307 break; 308 default: 309 break; 310 } 311 } 312 313 static int 314 acpi_cmbat_probe(device_t dev) 315 { 316 if (acpi_get_type(dev) == ACPI_TYPE_DEVICE && 317 !acpi_disabled("cmbat") && acpi_MatchHid(dev, "PNP0C0A")) { 318 319 device_set_desc(dev, "Control Method Battery"); 320 return (0); 321 } 322 return (ENXIO); 323 } 324 325 static int 326 acpi_cmbat_attach(device_t dev) 327 { 328 int error; 329 ACPI_HANDLE handle; 330 struct acpi_cmbat_softc *sc; 331 332 if ((sc = device_get_softc(dev)) == NULL) 333 return (ENXIO); 334 335 handle = acpi_get_handle(dev); 336 337 /* 338 * Install a system notify handler in addition to the device notify. 339 * Toshiba notebook uses this alternate notify for its battery. 340 */ 341 AcpiInstallNotifyHandler(handle, ACPI_SYSTEM_NOTIFY, 342 acpi_cmbat_notify_handler, dev); 343 AcpiInstallNotifyHandler(handle, ACPI_DEVICE_NOTIFY, 344 acpi_cmbat_notify_handler, dev); 345 346 sc->bif_updating = sc->bst_updating = 0; 347 sc->dev = dev; 348 349 timespecclear(&sc->bif_lastupdated); 350 timespecclear(&sc->bst_lastupdated); 351 352 if (acpi_cmbat_units == 0) { 353 error = acpi_register_ioctl(ACPIIO_CMBAT_GET_BIF, 354 acpi_cmbat_ioctl, NULL); 355 if (error != 0) 356 return (error); 357 error = acpi_register_ioctl(ACPIIO_CMBAT_GET_BST, 358 acpi_cmbat_ioctl, NULL); 359 if (error != 0) 360 return (error); 361 } 362 363 error = acpi_battery_register(ACPI_BATT_TYPE_CMBAT, acpi_cmbat_units); 364 if (error != 0) 365 return (error); 366 367 acpi_cmbat_units++; 368 timespecclear(&acpi_cmbat_info_lastupdated); 369 sc->initializing = 0; 370 AcpiOsQueueForExecution(OSD_PRIORITY_LO, acpi_cmbat_init_battery, dev); 371 372 return (0); 373 } 374 375 static int 376 acpi_cmbat_resume(device_t dev) 377 { 378 AcpiOsQueueForExecution(OSD_PRIORITY_LO, acpi_cmbat_init_battery, dev); 379 return (0); 380 } 381 382 static int 383 acpi_cmbat_ioctl(u_long cmd, caddr_t addr, void *arg) 384 { 385 device_t dev; 386 union acpi_battery_ioctl_arg *ioctl_arg; 387 struct acpi_cmbat_softc *sc; 388 struct acpi_bif *bifp; 389 struct acpi_bst *bstp; 390 391 ioctl_arg = (union acpi_battery_ioctl_arg *)addr; 392 dev = devclass_get_device(acpi_cmbat_devclass, ioctl_arg->unit); 393 if (dev == NULL) 394 return (ENXIO); 395 sc = device_get_softc(dev); 396 if (sc == NULL) 397 return (ENXIO); 398 399 /* 400 * No security check required: information retrieval only. If 401 * new functions are added here, a check might be required. 402 */ 403 switch (cmd) { 404 case ACPIIO_CMBAT_GET_BIF: 405 acpi_cmbat_get_bif(dev); 406 bifp = &ioctl_arg->bif; 407 bifp->units = sc->bif.units; 408 bifp->dcap = sc->bif.dcap; 409 bifp->lfcap = sc->bif.lfcap; 410 bifp->btech = sc->bif.btech; 411 bifp->dvol = sc->bif.dvol; 412 bifp->wcap = sc->bif.wcap; 413 bifp->lcap = sc->bif.lcap; 414 bifp->gra1 = sc->bif.gra1; 415 bifp->gra2 = sc->bif.gra2; 416 strncpy(bifp->model, sc->bif.model, sizeof(sc->bif.model)); 417 strncpy(bifp->serial, sc->bif.serial, sizeof(sc->bif.serial)); 418 strncpy(bifp->type, sc->bif.type, sizeof(sc->bif.type)); 419 strncpy(bifp->oeminfo, sc->bif.oeminfo, sizeof(sc->bif.oeminfo)); 420 break; 421 case ACPIIO_CMBAT_GET_BST: 422 bstp = &ioctl_arg->bst; 423 if (acpi_BatteryIsPresent(dev)) { 424 acpi_cmbat_get_bst(dev); 425 bstp->state = sc->bst.state; 426 bstp->rate = sc->bst.rate; 427 bstp->cap = sc->bst.cap; 428 bstp->volt = sc->bst.volt; 429 } else { 430 bstp->state = ACPI_BATT_STAT_NOT_PRESENT; 431 } 432 break; 433 default: 434 break; 435 } 436 437 return (0); 438 } 439 440 static int 441 acpi_cmbat_is_bst_valid(struct acpi_bst *bst) 442 { 443 if (bst->state >= ACPI_BATT_STAT_MAX || bst->cap == 0xffffffff || 444 bst->volt == 0xffffffff) 445 446 return (0); 447 else 448 return (1); 449 } 450 451 static int 452 acpi_cmbat_is_bif_valid(struct acpi_bif *bif) 453 { 454 if (bif->lfcap == 0) 455 return (0); 456 else 457 return (1); 458 } 459 460 static int 461 acpi_cmbat_get_total_battinfo(struct acpi_battinfo *battinfo) 462 { 463 int i; 464 int error; 465 int batt_stat; 466 int valid_rate, valid_units; 467 int cap, min; 468 int total_cap, total_min, total_full; 469 device_t dev; 470 struct acpi_cmbat_softc *sc; 471 static int bat_units = 0; 472 static struct acpi_cmbat_softc **bat = NULL; 473 474 cap = min = -1; 475 batt_stat = ACPI_BATT_STAT_NOT_PRESENT; 476 error = 0; 477 478 /* Allocate array of softc pointers */ 479 if (bat_units != acpi_cmbat_units) { 480 if (bat != NULL) { 481 free(bat, M_ACPICMBAT); 482 bat = NULL; 483 } 484 bat_units = 0; 485 } 486 if (bat == NULL) { 487 bat_units = acpi_cmbat_units; 488 bat = malloc(sizeof(struct acpi_cmbat_softc *) * bat_units, 489 M_ACPICMBAT, M_NOWAIT); 490 if (bat == NULL) { 491 error = ENOMEM; 492 goto out; 493 } 494 495 /* Collect softc pointers */ 496 for (i = 0; i < acpi_cmbat_units; i++) { 497 if ((dev = devclass_get_device(acpi_cmbat_devclass, i)) == NULL) { 498 error = ENXIO; 499 goto out; 500 } 501 if ((sc = device_get_softc(dev)) == NULL) { 502 error = ENXIO; 503 goto out; 504 } 505 bat[i] = sc; 506 } 507 } 508 509 /* Get battery status, valid rate and valid units */ 510 batt_stat = valid_rate = valid_units = 0; 511 for (i = 0; i < acpi_cmbat_units; i++) { 512 bat[i]->present = acpi_BatteryIsPresent(bat[i]->dev); 513 if (!bat[i]->present) 514 continue; 515 516 acpi_cmbat_get_bst(bat[i]->dev); 517 518 /* If battery not installed, we get strange values */ 519 if (!acpi_cmbat_is_bst_valid(&(bat[i]->bst)) || 520 !acpi_cmbat_is_bif_valid(&(bat[i]->bif))) { 521 522 bat[i]->present = 0; 523 continue; 524 } 525 526 valid_units++; 527 bat[i]->cap = 100 * bat[i]->bst.cap / bat[i]->bif.lfcap; 528 batt_stat |= bat[i]->bst.state; 529 530 if (bat[i]->bst.rate > 0) { 531 /* 532 * XXX Hack to calculate total battery time. 533 * Systems with 2 or more battries, they may get used 534 * one by one, thus bst.rate is set only to the one 535 * in use. For remaining batteries bst.rate = 0, which 536 * makes it impossible to calculate remaining time. 537 * Some other systems may need sum of bst.rate in 538 * dis-charging state. 539 * There for we sum up the bst.rate that is valid 540 * (in dis-charging state), and use the sum to 541 * calcutate remaining batteries' time. 542 */ 543 if (bat[i]->bst.state & ACPI_BATT_STAT_DISCHARG) 544 valid_rate += bat[i]->bst.rate; 545 } 546 } 547 548 /* Calculate total battery capacity and time */ 549 total_cap = total_min = total_full = 0; 550 for (i = 0; i < acpi_cmbat_units; i++) { 551 if (!bat[i]->present) 552 continue; 553 554 if (valid_rate > 0) { 555 /* Use the sum of bst.rate */ 556 bat[i]->min = 60 * bat[i]->bst.cap / valid_rate; 557 } else if (bat[i]->full_charge_time > 0) { 558 bat[i]->min = (bat[i]->full_charge_time * bat[i]->cap) / 100; 559 } else { 560 /* Couldn't find valid rate and full battery time */ 561 bat[i]->min = 0; 562 } 563 total_min += bat[i]->min; 564 total_cap += bat[i]->cap; 565 total_full += bat[i]->full_charge_time; 566 } 567 568 /* Battery life */ 569 if (valid_units == 0) { 570 cap = -1; 571 batt_stat = ACPI_BATT_STAT_NOT_PRESENT; 572 } else { 573 cap = total_cap / valid_units; 574 } 575 576 /* Battery time */ 577 if (valid_units == 0) { 578 min = -1; 579 } else if (valid_rate == 0 || (batt_stat & ACPI_BATT_STAT_CHARGING)) { 580 if (total_full == 0) 581 min = -1; 582 else 583 min = (total_full * cap) / 100; 584 } else { 585 min = total_min; 586 } 587 acpi_cmbat_info_updated(&acpi_cmbat_info_lastupdated); 588 589 out: 590 battinfo->cap = cap; 591 battinfo->min = min; 592 battinfo->state = batt_stat; 593 594 return (error); 595 } 596 597 static void 598 acpi_cmbat_init_battery(void *arg) 599 { 600 int retry; 601 device_t dev = (device_t)arg; 602 struct acpi_cmbat_softc *sc = device_get_softc(dev); 603 604 if (sc->initializing) 605 return; 606 607 sc->initializing = 1; 608 ACPI_VPRINT(dev, acpi_device_get_parent_softc(dev), 609 "battery initialization start\n"); 610 611 for (retry = 0; retry < ACPI_CMBAT_RETRY_MAX; retry++, AcpiOsSleep(10, 0)) { 612 sc->present = acpi_BatteryIsPresent(dev); 613 if (!sc->present) 614 continue; 615 616 timespecclear(&sc->bst_lastupdated); 617 timespecclear(&sc->bif_lastupdated); 618 619 acpi_cmbat_get_bst(dev); 620 if (!acpi_cmbat_is_bst_valid(&sc->bst)) 621 continue; 622 623 acpi_cmbat_get_bif(dev); 624 if (!acpi_cmbat_is_bif_valid(&sc->bif)) 625 continue; 626 break; 627 } 628 629 sc->initializing = 0; 630 if (retry == ACPI_CMBAT_RETRY_MAX) { 631 ACPI_VPRINT(dev, acpi_device_get_parent_softc(dev), 632 "battery initialization failed, giving up\n"); 633 } else { 634 ACPI_VPRINT(dev, acpi_device_get_parent_softc(dev), 635 "battery initialization done, tried %d times\n", retry + 1); 636 } 637 } 638 639 /* 640 * Public interfaces. 641 */ 642 int 643 acpi_cmbat_get_battinfo(int unit, struct acpi_battinfo *battinfo) 644 { 645 int error; 646 device_t dev; 647 struct acpi_cmbat_softc *sc; 648 649 if (unit == -1) 650 return (acpi_cmbat_get_total_battinfo(battinfo)); 651 652 if (acpi_cmbat_info_expired(&acpi_cmbat_info_lastupdated)) { 653 error = acpi_cmbat_get_total_battinfo(battinfo); 654 if (error) 655 goto out; 656 } 657 658 error = 0; 659 if (unit >= acpi_cmbat_units) { 660 error = ENXIO; 661 goto out; 662 } 663 664 if ((dev = devclass_get_device(acpi_cmbat_devclass, unit)) == NULL) { 665 error = ENXIO; 666 goto out; 667 } 668 if ((sc = device_get_softc(dev)) == NULL) { 669 error = ENXIO; 670 goto out; 671 } 672 673 if (!sc->present) { 674 battinfo->cap = -1; 675 battinfo->min = -1; 676 battinfo->state = ACPI_BATT_STAT_NOT_PRESENT; 677 } else { 678 battinfo->cap = sc->cap; 679 battinfo->min = sc->min; 680 battinfo->state = sc->bst.state; 681 } 682 683 out: 684 return (error); 685 } 686