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