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 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 __func__ ": PKG_GETINT error, idx = %d\n.", 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 __func__ ": PKG_GETSTR error, idx = %d\n.", 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 ACPI_BUFFER bif_buffer; 108 ACPI_BUFFER bst_buffer; 109 struct timespec bif_lastupdated; 110 struct timespec bst_lastupdated; 111 int bif_updating; 112 int bst_updating; 113 114 int not_present; 115 int cap; 116 int min; 117 int full_charge_time; 118 119 struct callout_handle cmbat_timeout; 120 }; 121 122 static struct timespec acpi_cmbat_info_lastupdated; 123 124 /* XXX: devclass_get_maxunit() don't give us the current allocated units... */ 125 static int acpi_cmbat_units = 0; 126 127 static void acpi_cmbat_timeout(void *); 128 static int acpi_cmbat_info_expired(struct timespec *); 129 static void acpi_cmbat_info_updated(struct timespec *); 130 static void acpi_cmbat_get_bst(void *); 131 static void acpi_cmbat_get_bif(void *); 132 static void acpi_cmbat_notify_handler(ACPI_HANDLE, UINT32, void *); 133 static int acpi_cmbat_probe(device_t); 134 static int acpi_cmbat_attach(device_t); 135 static int acpi_cmbat_resume(device_t); 136 static int acpi_cmbat_ioctl(u_long, caddr_t, void *); 137 static int acpi_cmbat_get_total_battinfo(struct acpi_battinfo *); 138 139 /* 140 * Poll the battery info. 141 */ 142 static void 143 acpi_cmbat_timeout(void *context) 144 { 145 device_t dev; 146 struct acpi_cmbat_softc *sc; 147 148 dev = (device_t)context; 149 sc = device_get_softc(dev); 150 151 AcpiOsQueueForExecution(OSD_PRIORITY_LO, acpi_cmbat_get_bif, dev); 152 sc->cmbat_timeout = timeout(acpi_cmbat_timeout, dev, CMBAT_POLLRATE); 153 } 154 155 static __inline int 156 acpi_cmbat_info_expired(struct timespec *lastupdated) 157 { 158 struct timespec curtime; 159 160 if (lastupdated == NULL) { 161 return (1); 162 } 163 164 if (!timespecisset(lastupdated)) { 165 return (1); 166 } 167 168 getnanotime(&curtime); 169 timespecsub(&curtime, lastupdated); 170 return ((curtime.tv_sec < 0 || curtime.tv_sec > acpi_battery_get_info_expire())); 171 } 172 173 174 static __inline void 175 acpi_cmbat_info_updated(struct timespec *lastupdated) 176 { 177 178 if (lastupdated != NULL) { 179 getnanotime(lastupdated); 180 } 181 } 182 183 static void 184 acpi_cmbat_get_bst(void *context) 185 { 186 device_t dev; 187 struct acpi_cmbat_softc *sc; 188 ACPI_STATUS as; 189 ACPI_OBJECT *res, *tmp; 190 ACPI_HANDLE h; 191 192 dev = context; 193 sc = device_get_softc(dev); 194 h = acpi_get_handle(dev); 195 196 if (!acpi_cmbat_info_expired(&sc->bst_lastupdated)) { 197 return; 198 } 199 200 if (sc->bst_updating) { 201 return; 202 } 203 sc->bst_updating = 1; 204 205 untimeout(acpi_cmbat_timeout, (caddr_t)dev, sc->cmbat_timeout); 206 207 if ((as = acpi_EvaluateIntoBuffer(h, "_BST", NULL, &sc->bst_buffer)) != AE_OK) { 208 ACPI_VPRINT(dev, acpi_device_get_parent_softc(dev), 209 "error fetching current battery status -- %s\n", 210 AcpiFormatException(as)); 211 goto end; 212 } 213 214 res = (ACPI_OBJECT *)sc->bst_buffer.Pointer; 215 216 if ((res->Type != ACPI_TYPE_PACKAGE) || (res->Package.Count != 4)) { 217 ACPI_VPRINT(dev, acpi_device_get_parent_softc(dev), 218 "battery status corrupted\n"); 219 goto end; 220 } 221 222 PKG_GETINT(res, tmp, 0, sc->bst.state, end); 223 PKG_GETINT(res, tmp, 1, sc->bst.rate, end); 224 PKG_GETINT(res, tmp, 2, sc->bst.cap, end); 225 PKG_GETINT(res, tmp, 3, sc->bst.volt, end); 226 acpi_cmbat_info_updated(&sc->bst_lastupdated); 227 end: 228 sc->bst_updating = 0; 229 sc->cmbat_timeout = timeout(acpi_cmbat_timeout, dev, CMBAT_POLLRATE); 230 } 231 232 static void 233 acpi_cmbat_get_bif(void *context) 234 { 235 device_t dev; 236 struct acpi_cmbat_softc *sc; 237 ACPI_STATUS as; 238 ACPI_OBJECT *res, *tmp; 239 ACPI_HANDLE h; 240 241 dev = context; 242 sc = device_get_softc(dev); 243 h = acpi_get_handle(dev); 244 245 if (!acpi_cmbat_info_expired(&sc->bif_lastupdated)) { 246 return; 247 } 248 249 if (sc->bif_updating) { 250 return; 251 } 252 sc->bif_updating = 1; 253 254 untimeout(acpi_cmbat_timeout, (caddr_t)dev, sc->cmbat_timeout); 255 256 if ((as = acpi_EvaluateIntoBuffer(h, "_BIF", NULL, &sc->bif_buffer)) != AE_OK) { 257 ACPI_VPRINT(dev, acpi_device_get_parent_softc(dev), 258 "error fetching current battery info -- %s\n", 259 AcpiFormatException(as)); 260 goto end; 261 } 262 263 res = (ACPI_OBJECT *)sc->bif_buffer.Pointer; 264 265 if ((res->Type != ACPI_TYPE_PACKAGE) || (res->Package.Count != 13)) { 266 ACPI_VPRINT(dev, acpi_device_get_parent_softc(dev), 267 "battery info corrupted\n"); 268 goto end; 269 } 270 271 PKG_GETINT(res, tmp, 0, sc->bif.unit, end); 272 PKG_GETINT(res, tmp, 1, sc->bif.dcap, end); 273 PKG_GETINT(res, tmp, 2, sc->bif.lfcap, end); 274 PKG_GETINT(res, tmp, 3, sc->bif.btech, end); 275 PKG_GETINT(res, tmp, 4, sc->bif.dvol, end); 276 PKG_GETINT(res, tmp, 5, sc->bif.wcap, end); 277 PKG_GETINT(res, tmp, 6, sc->bif.lcap, end); 278 PKG_GETINT(res, tmp, 7, sc->bif.gra1, end); 279 PKG_GETINT(res, tmp, 8, sc->bif.gra2, end); 280 PKG_GETSTR(res, tmp, 9, sc->bif.model, ACPI_CMBAT_MAXSTRLEN, end); 281 PKG_GETSTR(res, tmp, 10, sc->bif.serial, ACPI_CMBAT_MAXSTRLEN, end); 282 PKG_GETSTR(res, tmp, 11, sc->bif.type, ACPI_CMBAT_MAXSTRLEN, end); 283 PKG_GETSTR(res, tmp, 12, sc->bif.oeminfo, ACPI_CMBAT_MAXSTRLEN, end); 284 acpi_cmbat_info_updated(&sc->bif_lastupdated); 285 end: 286 sc->bif_updating = 0; 287 sc->cmbat_timeout = timeout(acpi_cmbat_timeout, dev, CMBAT_POLLRATE); 288 } 289 290 static void 291 acpi_cmbat_notify_handler(ACPI_HANDLE h, UINT32 notify, void *context) 292 { 293 device_t dev; 294 struct acpi_cmbat_softc *sc; 295 296 dev = (device_t)context; 297 if ((sc = device_get_softc(dev)) == NULL) { 298 return; 299 } 300 301 switch (notify) { 302 case ACPI_BATTERY_BST_CHANGE: 303 timespecclear(&sc->bst_lastupdated); 304 break; 305 case ACPI_BATTERY_BIF_CHANGE: 306 timespecclear(&sc->bif_lastupdated); 307 AcpiOsQueueForExecution(OSD_PRIORITY_LO, acpi_cmbat_get_bif, dev); 308 break; 309 default: 310 break; 311 } 312 } 313 314 static int 315 acpi_cmbat_probe(device_t dev) 316 { 317 318 if ((acpi_get_type(dev) == ACPI_TYPE_DEVICE) && 319 !acpi_disabled("cmbat") && 320 acpi_MatchHid(dev, "PNP0C0A")) { 321 /* 322 * Set device description. 323 */ 324 device_set_desc(dev, "Control method Battery"); 325 return (0); 326 } 327 return (ENXIO); 328 } 329 330 static int 331 acpi_cmbat_attach(device_t dev) 332 { 333 int error; 334 ACPI_HANDLE handle; 335 struct acpi_cmbat_softc *sc; 336 337 if ((sc = device_get_softc(dev)) == NULL) { 338 return (ENXIO); 339 } 340 341 handle = acpi_get_handle(dev); 342 343 AcpiInstallNotifyHandler(handle, ACPI_DEVICE_NOTIFY, 344 acpi_cmbat_notify_handler, dev); 345 346 bzero(&sc->bif_buffer, sizeof(sc->bif_buffer)); 347 bzero(&sc->bst_buffer, sizeof(sc->bst_buffer)); 348 sc->bif_updating = sc->bst_updating = 0; 349 sc->dev = dev; 350 351 timespecclear(&sc->bif_lastupdated); 352 timespecclear(&sc->bst_lastupdated); 353 354 if (acpi_cmbat_units == 0) { 355 if ((error = acpi_register_ioctl(ACPIIO_CMBAT_GET_BIF, 356 acpi_cmbat_ioctl, NULL)) != 0) { 357 return (error); 358 } 359 if ((error = acpi_register_ioctl(ACPIIO_CMBAT_GET_BST, 360 acpi_cmbat_ioctl, NULL)) != 0) { 361 return (error); 362 } 363 } 364 365 if ((error = acpi_battery_register(ACPI_BATT_TYPE_CMBAT, 366 acpi_cmbat_units)) != 0) { 367 return (error); 368 } 369 370 acpi_cmbat_units++; 371 timespecclear(&acpi_cmbat_info_lastupdated); 372 373 AcpiOsQueueForExecution(OSD_PRIORITY_LO, acpi_cmbat_get_bif, dev); 374 return (0); 375 } 376 377 static int 378 acpi_cmbat_resume(device_t dev) 379 { 380 381 AcpiOsQueueForExecution(OSD_PRIORITY_LO, acpi_cmbat_get_bif, dev); 382 return (0); 383 } 384 385 static device_method_t acpi_cmbat_methods[] = { 386 /* Device interface */ 387 DEVMETHOD(device_probe, acpi_cmbat_probe), 388 DEVMETHOD(device_attach, acpi_cmbat_attach), 389 DEVMETHOD(device_resume, acpi_cmbat_resume), 390 391 {0, 0} 392 }; 393 394 static driver_t acpi_cmbat_driver = { 395 "acpi_cmbat", 396 acpi_cmbat_methods, 397 sizeof(struct acpi_cmbat_softc), 398 }; 399 400 static devclass_t acpi_cmbat_devclass; 401 DRIVER_MODULE(acpi_cmbat, acpi, acpi_cmbat_driver, acpi_cmbat_devclass, 0, 0); 402 403 static int 404 acpi_cmbat_ioctl(u_long cmd, caddr_t addr, void *arg) 405 { 406 device_t dev; 407 union acpi_battery_ioctl_arg *ioctl_arg; 408 struct acpi_cmbat_softc *sc; 409 struct acpi_bif *bifp; 410 struct acpi_bst *bstp; 411 412 ioctl_arg = (union acpi_battery_ioctl_arg *)addr; 413 if ((dev = devclass_get_device(acpi_cmbat_devclass, 414 ioctl_arg->unit)) == NULL) { 415 return (ENXIO); 416 } 417 418 if ((sc = device_get_softc(dev)) == NULL) { 419 return (ENXIO); 420 } 421 422 switch (cmd) { 423 case ACPIIO_CMBAT_GET_BIF: 424 acpi_cmbat_get_bif(dev); 425 bifp = &ioctl_arg->bif; 426 bifp->unit = sc->bif.unit; 427 bifp->dcap = sc->bif.dcap; 428 bifp->lfcap = sc->bif.lfcap; 429 bifp->btech = sc->bif.btech; 430 bifp->dvol = sc->bif.dvol; 431 bifp->wcap = sc->bif.wcap; 432 bifp->lcap = sc->bif.lcap; 433 bifp->gra1 = sc->bif.gra1; 434 bifp->gra2 = sc->bif.gra2; 435 strncpy(bifp->model, sc->bif.model, sizeof(sc->bif.model)); 436 strncpy(bifp->serial, sc->bif.serial, sizeof(sc->bif.serial)); 437 strncpy(bifp->type, sc->bif.type, sizeof(sc->bif.type)); 438 strncpy(bifp->oeminfo, sc->bif.oeminfo, sizeof(sc->bif.oeminfo)); 439 break; 440 441 case ACPIIO_CMBAT_GET_BST: 442 acpi_cmbat_get_bst(dev); 443 bstp = &ioctl_arg->bst; 444 bstp->state = sc->bst.state; 445 bstp->rate = sc->bst.rate; 446 bstp->cap = sc->bst.cap; 447 bstp->volt = sc->bst.volt; 448 break; 449 } 450 451 return (0); 452 } 453 454 static int 455 acpi_cmbat_get_total_battinfo(struct acpi_battinfo *battinfo) 456 { 457 int i; 458 int error; 459 int batt_stat; 460 int valid_rate, valid_units; 461 int cap, min; 462 int total_cap, total_min, total_full; 463 device_t dev; 464 struct acpi_cmbat_softc *sc; 465 static int bat_units = 0; 466 static struct acpi_cmbat_softc **bat = NULL; 467 468 cap = min = -1; 469 batt_stat = ACPI_BATT_STAT_NOT_PRESENT; 470 error = 0; 471 472 /* Allocate array of softc pointers */ 473 if (bat_units != acpi_cmbat_units) { 474 if (bat != NULL) { 475 free(bat, M_ACPICMBAT); 476 bat = NULL; 477 } 478 bat_units = 0; 479 } 480 if (bat == NULL) { 481 bat_units = acpi_cmbat_units; 482 bat = malloc(sizeof(struct acpi_cmbat_softc *) * bat_units, 483 M_ACPICMBAT, M_NOWAIT); 484 if (bat == NULL) { 485 error = ENOMEM; 486 goto out; 487 } 488 489 /* Collect softc pointers */ 490 for (i = 0; i < acpi_cmbat_units; i++) { 491 if ((dev = devclass_get_device(acpi_cmbat_devclass, i)) == NULL) { 492 error = ENXIO; 493 goto out; 494 } 495 496 if ((sc = device_get_softc(dev)) == NULL) { 497 error = ENXIO; 498 goto out; 499 } 500 501 bat[i] = sc; 502 } 503 } 504 505 /* Get battery status, valid rate and valid units */ 506 batt_stat = valid_rate = valid_units = 0; 507 for (i = 0; i < acpi_cmbat_units; i++) { 508 bat[i]->not_present = 0; 509 acpi_cmbat_get_bst(bat[i]->dev); 510 511 /* If battey not installed, we get strange values */ 512 if (bat[i]->bst.state >= ACPI_BATT_STAT_MAX || 513 bat[i]->bst.cap == 0xffffffff || 514 bat[i]->bst.volt == 0xffffffff || 515 bat[i]->bif.lfcap == 0) { 516 bat[i]->not_present = 1; 517 continue; 518 } 519 520 valid_units++; 521 522 bat[i]->cap = 100 * bat[i]->bst.cap / bat[i]->bif.lfcap; 523 524 batt_stat |= bat[i]->bst.state; 525 526 if (bat[i]->bst.rate > 0) { 527 /* 528 * XXX Hack to calculate total battery time. 529 * Systems with 2 or more battries, they may get used 530 * one by one, thus bst.rate is set only to the one 531 * in use. For remaining batteries bst.rate = 0, which 532 * makes it impossible to calculate remaining time. 533 * Some other systems may need sum of bst.rate in 534 * dis-charging state. 535 * There for we sum up the bst.rate that is valid 536 * (in dis-charging state), and use the sum to 537 * calcutate remaining batteries' time. 538 */ 539 if (bat[i]->bst.state & ACPI_BATT_STAT_DISCHARG) { 540 valid_rate += bat[i]->bst.rate; 541 } 542 } 543 } 544 545 /* Calculate total battery capacity and time */ 546 total_cap = total_min = total_full = 0; 547 for (i = 0; i < acpi_cmbat_units; i++) { 548 if (bat[i]->not_present) { 549 continue; 550 } 551 552 if (valid_rate > 0) { 553 /* Use the sum of bst.rate */ 554 bat[i]->min = 60 * bat[i]->bst.cap / valid_rate; 555 } else if (bat[i]->full_charge_time > 0) { 556 bat[i]->min = (bat[i]->full_charge_time * bat[i]->cap) / 100; 557 } else { 558 /* Couldn't find valid rate and full battery time */ 559 bat[i]->min = 0; 560 } 561 total_min += bat[i]->min; 562 total_cap += bat[i]->cap; 563 total_full += bat[i]->full_charge_time; 564 } 565 566 /* Battery life */ 567 if (valid_units == 0) { 568 cap = -1; 569 batt_stat = ACPI_BATT_STAT_NOT_PRESENT; 570 } else { 571 cap = total_cap / valid_units; 572 } 573 574 /* Battery time */ 575 if (valid_units == 0) { 576 min = -1; 577 } else if (valid_rate == 0 || (batt_stat & ACPI_BATT_STAT_CHARGING)) { 578 if (total_full == 0) { 579 min = -1; 580 } else { 581 min = (total_full * cap) / 100; 582 } 583 } else { 584 min = total_min; 585 } 586 587 acpi_cmbat_info_updated(&acpi_cmbat_info_lastupdated); 588 out: 589 battinfo->cap = cap; 590 battinfo->min = min; 591 battinfo->state = batt_stat; 592 593 return (error); 594 } 595 596 /* 597 * Public interfaces. 598 */ 599 600 int 601 acpi_cmbat_get_battinfo(int unit, struct acpi_battinfo *battinfo) 602 { 603 int error; 604 device_t dev; 605 struct acpi_cmbat_softc *sc; 606 607 if (unit == -1) { 608 return (acpi_cmbat_get_total_battinfo(battinfo)); 609 } 610 611 if (acpi_cmbat_info_expired(&acpi_cmbat_info_lastupdated)) { 612 error = acpi_cmbat_get_total_battinfo(battinfo); 613 if (error) { 614 goto out; 615 } 616 } 617 618 error = 0; 619 if (unit >= acpi_cmbat_units) { 620 error = ENXIO; 621 goto out; 622 } 623 624 if ((dev = devclass_get_device(acpi_cmbat_devclass, unit)) == NULL) { 625 error = ENXIO; 626 goto out; 627 } 628 629 if ((sc = device_get_softc(dev)) == NULL) { 630 error = ENXIO; 631 goto out; 632 } 633 634 if (sc->not_present) { 635 battinfo->cap = -1; 636 battinfo->min = -1; 637 battinfo->state = ACPI_BATT_STAT_NOT_PRESENT; 638 } else { 639 battinfo->cap = sc->cap; 640 battinfo->min = sc->min; 641 battinfo->state = sc->bst.state; 642 } 643 out: 644 return (error); 645 } 646 647