1/*- 2 * Copyright (c) 2005 Nate Lawson 3 * Copyright (c) 2000 Munehiro Matsuda 4 * Copyright (c) 2000 Takanori Watanabe 5 * Copyright (c) 2000 Mitsuru IWASAKI <iwasaki@FreeBSD.org> 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 */ 29 30#include <sys/cdefs.h>
| 1/*- 2 * Copyright (c) 2005 Nate Lawson 3 * Copyright (c) 2000 Munehiro Matsuda 4 * Copyright (c) 2000 Takanori Watanabe 5 * Copyright (c) 2000 Mitsuru IWASAKI <iwasaki@FreeBSD.org> 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 */ 29 30#include <sys/cdefs.h>
|
31__FBSDID("$FreeBSD: head/sys/dev/acpica/acpi_cmbat.c 152818 2005-11-26 07:36:53Z njl $");
| 31__FBSDID("$FreeBSD: head/sys/dev/acpica/acpi_cmbat.c 157774 2006-04-15 12:31:34Z iwasaki $");
|
32 33#include "opt_acpi.h" 34#include <sys/param.h> 35#include <sys/kernel.h> 36#include <sys/module.h> 37#include <sys/bus.h> 38#include <sys/ioccom.h> 39 40#include <machine/bus.h> 41#include <sys/rman.h> 42#include <sys/malloc.h> 43 44#include <contrib/dev/acpica/acpi.h> 45#include <dev/acpica/acpivar.h> 46#include <dev/acpica/acpiio.h> 47 48MALLOC_DEFINE(M_ACPICMBAT, "acpicmbat", "ACPI control method battery data"); 49 50/* Number of times to retry initialization before giving up. */ 51#define ACPI_CMBAT_RETRY_MAX 6 52 53/* Check the battery once a minute. */ 54#define CMBAT_POLLRATE (60 * hz) 55 56/* Hooks for the ACPI CA debugging infrastructure */ 57#define _COMPONENT ACPI_BATTERY 58ACPI_MODULE_NAME("BATTERY") 59 60#define ACPI_BATTERY_BST_CHANGE 0x80 61#define ACPI_BATTERY_BIF_CHANGE 0x81 62 63struct acpi_cmbat_softc { 64 device_t dev; 65 int flags; 66 67 struct acpi_bif bif; 68 struct acpi_bst bst; 69 struct timespec bst_lastupdated; 70}; 71 72ACPI_SERIAL_DECL(cmbat, "ACPI cmbat"); 73 74static int acpi_cmbat_probe(device_t dev); 75static int acpi_cmbat_attach(device_t dev); 76static int acpi_cmbat_detach(device_t dev); 77static int acpi_cmbat_resume(device_t dev); 78static void acpi_cmbat_notify_handler(ACPI_HANDLE h, UINT32 notify, 79 void *context); 80static int acpi_cmbat_info_expired(struct timespec *lastupdated); 81static void acpi_cmbat_info_updated(struct timespec *lastupdated); 82static void acpi_cmbat_get_bst(void *arg); 83static void acpi_cmbat_get_bif_task(void *arg); 84static void acpi_cmbat_get_bif(void *arg); 85static int acpi_cmbat_bst(device_t dev, struct acpi_bst *bstp); 86static int acpi_cmbat_bif(device_t dev, struct acpi_bif *bifp); 87static void acpi_cmbat_init_battery(void *arg); 88 89static device_method_t acpi_cmbat_methods[] = { 90 /* Device interface */ 91 DEVMETHOD(device_probe, acpi_cmbat_probe), 92 DEVMETHOD(device_attach, acpi_cmbat_attach), 93 DEVMETHOD(device_detach, acpi_cmbat_detach), 94 DEVMETHOD(device_resume, acpi_cmbat_resume), 95 96 /* ACPI battery interface */ 97 DEVMETHOD(acpi_batt_get_info, acpi_cmbat_bif), 98 DEVMETHOD(acpi_batt_get_status, acpi_cmbat_bst), 99 100 {0, 0} 101}; 102 103static driver_t acpi_cmbat_driver = { 104 "battery", 105 acpi_cmbat_methods, 106 sizeof(struct acpi_cmbat_softc), 107}; 108 109static devclass_t acpi_cmbat_devclass; 110DRIVER_MODULE(acpi_cmbat, acpi, acpi_cmbat_driver, acpi_cmbat_devclass, 0, 0); 111MODULE_DEPEND(acpi_cmbat, acpi, 1, 1, 1); 112 113static int 114acpi_cmbat_probe(device_t dev) 115{ 116 static char *cmbat_ids[] = { "PNP0C0A", NULL }; 117 118 if (acpi_disabled("cmbat") || 119 ACPI_ID_PROBE(device_get_parent(dev), dev, cmbat_ids) == NULL) 120 return (ENXIO); 121 122 device_set_desc(dev, "ACPI Control Method Battery"); 123 return (0); 124} 125 126static int 127acpi_cmbat_attach(device_t dev) 128{ 129 int error; 130 ACPI_HANDLE handle; 131 struct acpi_cmbat_softc *sc; 132 133 sc = device_get_softc(dev); 134 handle = acpi_get_handle(dev); 135 sc->dev = dev; 136 137 timespecclear(&sc->bst_lastupdated); 138 139 error = acpi_battery_register(dev); 140 if (error != 0) { 141 device_printf(dev, "registering battery failed\n"); 142 return (error); 143 } 144 145 /* 146 * Install a system notify handler in addition to the device notify. 147 * Toshiba notebook uses this alternate notify for its battery. 148 */ 149 AcpiInstallNotifyHandler(handle, ACPI_ALL_NOTIFY, 150 acpi_cmbat_notify_handler, dev); 151 152 AcpiOsQueueForExecution(OSD_PRIORITY_LO, acpi_cmbat_init_battery, dev); 153 154 return (0); 155} 156 157static int 158acpi_cmbat_detach(device_t dev) 159{ 160 161 acpi_battery_remove(dev);
| 32 33#include "opt_acpi.h" 34#include <sys/param.h> 35#include <sys/kernel.h> 36#include <sys/module.h> 37#include <sys/bus.h> 38#include <sys/ioccom.h> 39 40#include <machine/bus.h> 41#include <sys/rman.h> 42#include <sys/malloc.h> 43 44#include <contrib/dev/acpica/acpi.h> 45#include <dev/acpica/acpivar.h> 46#include <dev/acpica/acpiio.h> 47 48MALLOC_DEFINE(M_ACPICMBAT, "acpicmbat", "ACPI control method battery data"); 49 50/* Number of times to retry initialization before giving up. */ 51#define ACPI_CMBAT_RETRY_MAX 6 52 53/* Check the battery once a minute. */ 54#define CMBAT_POLLRATE (60 * hz) 55 56/* Hooks for the ACPI CA debugging infrastructure */ 57#define _COMPONENT ACPI_BATTERY 58ACPI_MODULE_NAME("BATTERY") 59 60#define ACPI_BATTERY_BST_CHANGE 0x80 61#define ACPI_BATTERY_BIF_CHANGE 0x81 62 63struct acpi_cmbat_softc { 64 device_t dev; 65 int flags; 66 67 struct acpi_bif bif; 68 struct acpi_bst bst; 69 struct timespec bst_lastupdated; 70}; 71 72ACPI_SERIAL_DECL(cmbat, "ACPI cmbat"); 73 74static int acpi_cmbat_probe(device_t dev); 75static int acpi_cmbat_attach(device_t dev); 76static int acpi_cmbat_detach(device_t dev); 77static int acpi_cmbat_resume(device_t dev); 78static void acpi_cmbat_notify_handler(ACPI_HANDLE h, UINT32 notify, 79 void *context); 80static int acpi_cmbat_info_expired(struct timespec *lastupdated); 81static void acpi_cmbat_info_updated(struct timespec *lastupdated); 82static void acpi_cmbat_get_bst(void *arg); 83static void acpi_cmbat_get_bif_task(void *arg); 84static void acpi_cmbat_get_bif(void *arg); 85static int acpi_cmbat_bst(device_t dev, struct acpi_bst *bstp); 86static int acpi_cmbat_bif(device_t dev, struct acpi_bif *bifp); 87static void acpi_cmbat_init_battery(void *arg); 88 89static device_method_t acpi_cmbat_methods[] = { 90 /* Device interface */ 91 DEVMETHOD(device_probe, acpi_cmbat_probe), 92 DEVMETHOD(device_attach, acpi_cmbat_attach), 93 DEVMETHOD(device_detach, acpi_cmbat_detach), 94 DEVMETHOD(device_resume, acpi_cmbat_resume), 95 96 /* ACPI battery interface */ 97 DEVMETHOD(acpi_batt_get_info, acpi_cmbat_bif), 98 DEVMETHOD(acpi_batt_get_status, acpi_cmbat_bst), 99 100 {0, 0} 101}; 102 103static driver_t acpi_cmbat_driver = { 104 "battery", 105 acpi_cmbat_methods, 106 sizeof(struct acpi_cmbat_softc), 107}; 108 109static devclass_t acpi_cmbat_devclass; 110DRIVER_MODULE(acpi_cmbat, acpi, acpi_cmbat_driver, acpi_cmbat_devclass, 0, 0); 111MODULE_DEPEND(acpi_cmbat, acpi, 1, 1, 1); 112 113static int 114acpi_cmbat_probe(device_t dev) 115{ 116 static char *cmbat_ids[] = { "PNP0C0A", NULL }; 117 118 if (acpi_disabled("cmbat") || 119 ACPI_ID_PROBE(device_get_parent(dev), dev, cmbat_ids) == NULL) 120 return (ENXIO); 121 122 device_set_desc(dev, "ACPI Control Method Battery"); 123 return (0); 124} 125 126static int 127acpi_cmbat_attach(device_t dev) 128{ 129 int error; 130 ACPI_HANDLE handle; 131 struct acpi_cmbat_softc *sc; 132 133 sc = device_get_softc(dev); 134 handle = acpi_get_handle(dev); 135 sc->dev = dev; 136 137 timespecclear(&sc->bst_lastupdated); 138 139 error = acpi_battery_register(dev); 140 if (error != 0) { 141 device_printf(dev, "registering battery failed\n"); 142 return (error); 143 } 144 145 /* 146 * Install a system notify handler in addition to the device notify. 147 * Toshiba notebook uses this alternate notify for its battery. 148 */ 149 AcpiInstallNotifyHandler(handle, ACPI_ALL_NOTIFY, 150 acpi_cmbat_notify_handler, dev); 151 152 AcpiOsQueueForExecution(OSD_PRIORITY_LO, acpi_cmbat_init_battery, dev); 153 154 return (0); 155} 156 157static int 158acpi_cmbat_detach(device_t dev) 159{ 160 161 acpi_battery_remove(dev);
|
| 162 AcpiRemoveNotifyHandler(handle, ACPI_ALL_NOTIFY, acpi_cmbat_notify_handler);
|
162 return (0); 163} 164 165static int 166acpi_cmbat_resume(device_t dev) 167{ 168 169 AcpiOsQueueForExecution(OSD_PRIORITY_LO, acpi_cmbat_init_battery, dev); 170 return (0); 171} 172 173static void 174acpi_cmbat_notify_handler(ACPI_HANDLE h, UINT32 notify, void *context) 175{ 176 struct acpi_cmbat_softc *sc; 177 device_t dev; 178 179 dev = (device_t)context; 180 sc = device_get_softc(dev); 181 182 switch (notify) { 183 case ACPI_NOTIFY_DEVICE_CHECK: 184 case ACPI_BATTERY_BST_CHANGE: 185 /* 186 * Clear the last updated time. The next call to retrieve the 187 * battery status will get the new value for us. 188 */ 189 timespecclear(&sc->bst_lastupdated); 190 break; 191 case ACPI_NOTIFY_BUS_CHECK: 192 case ACPI_BATTERY_BIF_CHANGE: 193 /* 194 * Queue a callback to get the current battery info from thread 195 * context. It's not safe to block in a notify handler. 196 */ 197 AcpiOsQueueForExecution(OSD_PRIORITY_LO, acpi_cmbat_get_bif_task, dev); 198 break; 199 } 200 201 acpi_UserNotify("CMBAT", h, notify); 202} 203 204static int 205acpi_cmbat_info_expired(struct timespec *lastupdated) 206{ 207 struct timespec curtime; 208 209 ACPI_SERIAL_ASSERT(cmbat); 210 211 if (lastupdated == NULL) 212 return (TRUE); 213 if (!timespecisset(lastupdated)) 214 return (TRUE); 215 216 getnanotime(&curtime); 217 timespecsub(&curtime, lastupdated); 218 return (curtime.tv_sec < 0 || 219 curtime.tv_sec > acpi_battery_get_info_expire()); 220} 221 222static void 223acpi_cmbat_info_updated(struct timespec *lastupdated) 224{ 225 226 ACPI_SERIAL_ASSERT(cmbat); 227 228 if (lastupdated != NULL) 229 getnanotime(lastupdated); 230} 231 232static void 233acpi_cmbat_get_bst(void *arg) 234{ 235 struct acpi_cmbat_softc *sc; 236 ACPI_STATUS as; 237 ACPI_OBJECT *res; 238 ACPI_HANDLE h; 239 ACPI_BUFFER bst_buffer; 240 device_t dev; 241 242 ACPI_SERIAL_ASSERT(cmbat); 243 244 dev = arg; 245 sc = device_get_softc(dev); 246 h = acpi_get_handle(dev); 247 bst_buffer.Pointer = NULL; 248 bst_buffer.Length = ACPI_ALLOCATE_BUFFER; 249 250 if (!acpi_cmbat_info_expired(&sc->bst_lastupdated)) 251 goto end; 252 253 as = AcpiEvaluateObject(h, "_BST", NULL, &bst_buffer); 254 if (ACPI_FAILURE(as)) { 255 ACPI_VPRINT(dev, acpi_device_get_parent_softc(dev), 256 "error fetching current battery status -- %s\n", 257 AcpiFormatException(as)); 258 goto end; 259 } 260 261 res = (ACPI_OBJECT *)bst_buffer.Pointer; 262 if (!ACPI_PKG_VALID(res, 4)) { 263 ACPI_VPRINT(dev, acpi_device_get_parent_softc(dev), 264 "battery status corrupted\n"); 265 goto end; 266 } 267 268 if (acpi_PkgInt32(res, 0, &sc->bst.state) != 0) 269 goto end; 270 if (acpi_PkgInt32(res, 1, &sc->bst.rate) != 0) 271 goto end; 272 if (acpi_PkgInt32(res, 2, &sc->bst.cap) != 0) 273 goto end; 274 if (acpi_PkgInt32(res, 3, &sc->bst.volt) != 0) 275 goto end; 276 acpi_cmbat_info_updated(&sc->bst_lastupdated); 277 278 /* XXX If all batteries are critical, perhaps we should suspend. */ 279 if (sc->bst.state & ACPI_BATT_STAT_CRITICAL) { 280 if ((sc->flags & ACPI_BATT_STAT_CRITICAL) == 0) { 281 sc->flags |= ACPI_BATT_STAT_CRITICAL; 282 device_printf(dev, "critically low charge!\n"); 283 } 284 } else 285 sc->flags &= ~ACPI_BATT_STAT_CRITICAL; 286 287end: 288 if (bst_buffer.Pointer != NULL) 289 AcpiOsFree(bst_buffer.Pointer); 290} 291 292/* XXX There should be a cleaner way to do this locking. */ 293static void 294acpi_cmbat_get_bif_task(void *arg) 295{ 296 297 ACPI_SERIAL_BEGIN(cmbat); 298 acpi_cmbat_get_bif(arg); 299 ACPI_SERIAL_END(cmbat); 300} 301 302static void 303acpi_cmbat_get_bif(void *arg) 304{ 305 struct acpi_cmbat_softc *sc; 306 ACPI_STATUS as; 307 ACPI_OBJECT *res; 308 ACPI_HANDLE h; 309 ACPI_BUFFER bif_buffer; 310 device_t dev; 311 312 ACPI_SERIAL_ASSERT(cmbat); 313 314 dev = arg; 315 sc = device_get_softc(dev); 316 h = acpi_get_handle(dev); 317 bif_buffer.Pointer = NULL; 318 bif_buffer.Length = ACPI_ALLOCATE_BUFFER; 319 320 as = AcpiEvaluateObject(h, "_BIF", NULL, &bif_buffer); 321 if (ACPI_FAILURE(as)) { 322 ACPI_VPRINT(dev, acpi_device_get_parent_softc(dev), 323 "error fetching current battery info -- %s\n", 324 AcpiFormatException(as)); 325 goto end; 326 } 327 328 res = (ACPI_OBJECT *)bif_buffer.Pointer; 329 if (!ACPI_PKG_VALID(res, 13)) { 330 ACPI_VPRINT(dev, acpi_device_get_parent_softc(dev), 331 "battery info corrupted\n"); 332 goto end; 333 } 334 335 if (acpi_PkgInt32(res, 0, &sc->bif.units) != 0) 336 goto end; 337 if (acpi_PkgInt32(res, 1, &sc->bif.dcap) != 0) 338 goto end; 339 if (acpi_PkgInt32(res, 2, &sc->bif.lfcap) != 0) 340 goto end; 341 if (acpi_PkgInt32(res, 3, &sc->bif.btech) != 0) 342 goto end; 343 if (acpi_PkgInt32(res, 4, &sc->bif.dvol) != 0) 344 goto end; 345 if (acpi_PkgInt32(res, 5, &sc->bif.wcap) != 0) 346 goto end; 347 if (acpi_PkgInt32(res, 6, &sc->bif.lcap) != 0) 348 goto end; 349 if (acpi_PkgInt32(res, 7, &sc->bif.gra1) != 0) 350 goto end; 351 if (acpi_PkgInt32(res, 8, &sc->bif.gra2) != 0) 352 goto end; 353 if (acpi_PkgStr(res, 9, sc->bif.model, ACPI_CMBAT_MAXSTRLEN) != 0) 354 goto end; 355 if (acpi_PkgStr(res, 10, sc->bif.serial, ACPI_CMBAT_MAXSTRLEN) != 0) 356 goto end; 357 if (acpi_PkgStr(res, 11, sc->bif.type, ACPI_CMBAT_MAXSTRLEN) != 0) 358 goto end; 359 if (acpi_PkgStr(res, 12, sc->bif.oeminfo, ACPI_CMBAT_MAXSTRLEN) != 0) 360 goto end; 361 362end: 363 if (bif_buffer.Pointer != NULL) 364 AcpiOsFree(bif_buffer.Pointer); 365} 366 367static int 368acpi_cmbat_bif(device_t dev, struct acpi_bif *bifp) 369{ 370 struct acpi_cmbat_softc *sc; 371 372 sc = device_get_softc(dev); 373 374 /* 375 * Just copy the data. The only value that should change is the 376 * last-full capacity, so we only update when we get a notify that says 377 * the info has changed. Many systems apparently take a long time to 378 * process a _BIF call so we avoid it if possible. 379 */ 380 ACPI_SERIAL_BEGIN(cmbat); 381 bifp->units = sc->bif.units; 382 bifp->dcap = sc->bif.dcap; 383 bifp->lfcap = sc->bif.lfcap; 384 bifp->btech = sc->bif.btech; 385 bifp->dvol = sc->bif.dvol; 386 bifp->wcap = sc->bif.wcap; 387 bifp->lcap = sc->bif.lcap; 388 bifp->gra1 = sc->bif.gra1; 389 bifp->gra2 = sc->bif.gra2; 390 strncpy(bifp->model, sc->bif.model, sizeof(sc->bif.model)); 391 strncpy(bifp->serial, sc->bif.serial, sizeof(sc->bif.serial)); 392 strncpy(bifp->type, sc->bif.type, sizeof(sc->bif.type)); 393 strncpy(bifp->oeminfo, sc->bif.oeminfo, sizeof(sc->bif.oeminfo)); 394 ACPI_SERIAL_END(cmbat); 395 396 return (0); 397} 398 399static int 400acpi_cmbat_bst(device_t dev, struct acpi_bst *bstp) 401{ 402 struct acpi_cmbat_softc *sc; 403 404 sc = device_get_softc(dev); 405 406 ACPI_SERIAL_BEGIN(cmbat); 407 if (acpi_BatteryIsPresent(dev)) { 408 acpi_cmbat_get_bst(dev); 409 bstp->state = sc->bst.state; 410 bstp->rate = sc->bst.rate; 411 bstp->cap = sc->bst.cap; 412 bstp->volt = sc->bst.volt; 413 } else 414 bstp->state = ACPI_BATT_STAT_NOT_PRESENT; 415 ACPI_SERIAL_END(cmbat); 416 417 return (0); 418} 419 420static void 421acpi_cmbat_init_battery(void *arg) 422{ 423 struct acpi_cmbat_softc *sc; 424 int retry, valid; 425 device_t dev; 426 427 dev = (device_t)arg; 428 sc = device_get_softc(dev); 429 ACPI_VPRINT(dev, acpi_device_get_parent_softc(dev), 430 "battery initialization start\n"); 431 432 /* 433 * Try repeatedly to get valid data from the battery. Since the 434 * embedded controller isn't always ready just after boot, we may have 435 * to wait a while. 436 */ 437 for (retry = 0; retry < ACPI_CMBAT_RETRY_MAX; retry++, AcpiOsSleep(10000)) {
| 163 return (0); 164} 165 166static int 167acpi_cmbat_resume(device_t dev) 168{ 169 170 AcpiOsQueueForExecution(OSD_PRIORITY_LO, acpi_cmbat_init_battery, dev); 171 return (0); 172} 173 174static void 175acpi_cmbat_notify_handler(ACPI_HANDLE h, UINT32 notify, void *context) 176{ 177 struct acpi_cmbat_softc *sc; 178 device_t dev; 179 180 dev = (device_t)context; 181 sc = device_get_softc(dev); 182 183 switch (notify) { 184 case ACPI_NOTIFY_DEVICE_CHECK: 185 case ACPI_BATTERY_BST_CHANGE: 186 /* 187 * Clear the last updated time. The next call to retrieve the 188 * battery status will get the new value for us. 189 */ 190 timespecclear(&sc->bst_lastupdated); 191 break; 192 case ACPI_NOTIFY_BUS_CHECK: 193 case ACPI_BATTERY_BIF_CHANGE: 194 /* 195 * Queue a callback to get the current battery info from thread 196 * context. It's not safe to block in a notify handler. 197 */ 198 AcpiOsQueueForExecution(OSD_PRIORITY_LO, acpi_cmbat_get_bif_task, dev); 199 break; 200 } 201 202 acpi_UserNotify("CMBAT", h, notify); 203} 204 205static int 206acpi_cmbat_info_expired(struct timespec *lastupdated) 207{ 208 struct timespec curtime; 209 210 ACPI_SERIAL_ASSERT(cmbat); 211 212 if (lastupdated == NULL) 213 return (TRUE); 214 if (!timespecisset(lastupdated)) 215 return (TRUE); 216 217 getnanotime(&curtime); 218 timespecsub(&curtime, lastupdated); 219 return (curtime.tv_sec < 0 || 220 curtime.tv_sec > acpi_battery_get_info_expire()); 221} 222 223static void 224acpi_cmbat_info_updated(struct timespec *lastupdated) 225{ 226 227 ACPI_SERIAL_ASSERT(cmbat); 228 229 if (lastupdated != NULL) 230 getnanotime(lastupdated); 231} 232 233static void 234acpi_cmbat_get_bst(void *arg) 235{ 236 struct acpi_cmbat_softc *sc; 237 ACPI_STATUS as; 238 ACPI_OBJECT *res; 239 ACPI_HANDLE h; 240 ACPI_BUFFER bst_buffer; 241 device_t dev; 242 243 ACPI_SERIAL_ASSERT(cmbat); 244 245 dev = arg; 246 sc = device_get_softc(dev); 247 h = acpi_get_handle(dev); 248 bst_buffer.Pointer = NULL; 249 bst_buffer.Length = ACPI_ALLOCATE_BUFFER; 250 251 if (!acpi_cmbat_info_expired(&sc->bst_lastupdated)) 252 goto end; 253 254 as = AcpiEvaluateObject(h, "_BST", NULL, &bst_buffer); 255 if (ACPI_FAILURE(as)) { 256 ACPI_VPRINT(dev, acpi_device_get_parent_softc(dev), 257 "error fetching current battery status -- %s\n", 258 AcpiFormatException(as)); 259 goto end; 260 } 261 262 res = (ACPI_OBJECT *)bst_buffer.Pointer; 263 if (!ACPI_PKG_VALID(res, 4)) { 264 ACPI_VPRINT(dev, acpi_device_get_parent_softc(dev), 265 "battery status corrupted\n"); 266 goto end; 267 } 268 269 if (acpi_PkgInt32(res, 0, &sc->bst.state) != 0) 270 goto end; 271 if (acpi_PkgInt32(res, 1, &sc->bst.rate) != 0) 272 goto end; 273 if (acpi_PkgInt32(res, 2, &sc->bst.cap) != 0) 274 goto end; 275 if (acpi_PkgInt32(res, 3, &sc->bst.volt) != 0) 276 goto end; 277 acpi_cmbat_info_updated(&sc->bst_lastupdated); 278 279 /* XXX If all batteries are critical, perhaps we should suspend. */ 280 if (sc->bst.state & ACPI_BATT_STAT_CRITICAL) { 281 if ((sc->flags & ACPI_BATT_STAT_CRITICAL) == 0) { 282 sc->flags |= ACPI_BATT_STAT_CRITICAL; 283 device_printf(dev, "critically low charge!\n"); 284 } 285 } else 286 sc->flags &= ~ACPI_BATT_STAT_CRITICAL; 287 288end: 289 if (bst_buffer.Pointer != NULL) 290 AcpiOsFree(bst_buffer.Pointer); 291} 292 293/* XXX There should be a cleaner way to do this locking. */ 294static void 295acpi_cmbat_get_bif_task(void *arg) 296{ 297 298 ACPI_SERIAL_BEGIN(cmbat); 299 acpi_cmbat_get_bif(arg); 300 ACPI_SERIAL_END(cmbat); 301} 302 303static void 304acpi_cmbat_get_bif(void *arg) 305{ 306 struct acpi_cmbat_softc *sc; 307 ACPI_STATUS as; 308 ACPI_OBJECT *res; 309 ACPI_HANDLE h; 310 ACPI_BUFFER bif_buffer; 311 device_t dev; 312 313 ACPI_SERIAL_ASSERT(cmbat); 314 315 dev = arg; 316 sc = device_get_softc(dev); 317 h = acpi_get_handle(dev); 318 bif_buffer.Pointer = NULL; 319 bif_buffer.Length = ACPI_ALLOCATE_BUFFER; 320 321 as = AcpiEvaluateObject(h, "_BIF", NULL, &bif_buffer); 322 if (ACPI_FAILURE(as)) { 323 ACPI_VPRINT(dev, acpi_device_get_parent_softc(dev), 324 "error fetching current battery info -- %s\n", 325 AcpiFormatException(as)); 326 goto end; 327 } 328 329 res = (ACPI_OBJECT *)bif_buffer.Pointer; 330 if (!ACPI_PKG_VALID(res, 13)) { 331 ACPI_VPRINT(dev, acpi_device_get_parent_softc(dev), 332 "battery info corrupted\n"); 333 goto end; 334 } 335 336 if (acpi_PkgInt32(res, 0, &sc->bif.units) != 0) 337 goto end; 338 if (acpi_PkgInt32(res, 1, &sc->bif.dcap) != 0) 339 goto end; 340 if (acpi_PkgInt32(res, 2, &sc->bif.lfcap) != 0) 341 goto end; 342 if (acpi_PkgInt32(res, 3, &sc->bif.btech) != 0) 343 goto end; 344 if (acpi_PkgInt32(res, 4, &sc->bif.dvol) != 0) 345 goto end; 346 if (acpi_PkgInt32(res, 5, &sc->bif.wcap) != 0) 347 goto end; 348 if (acpi_PkgInt32(res, 6, &sc->bif.lcap) != 0) 349 goto end; 350 if (acpi_PkgInt32(res, 7, &sc->bif.gra1) != 0) 351 goto end; 352 if (acpi_PkgInt32(res, 8, &sc->bif.gra2) != 0) 353 goto end; 354 if (acpi_PkgStr(res, 9, sc->bif.model, ACPI_CMBAT_MAXSTRLEN) != 0) 355 goto end; 356 if (acpi_PkgStr(res, 10, sc->bif.serial, ACPI_CMBAT_MAXSTRLEN) != 0) 357 goto end; 358 if (acpi_PkgStr(res, 11, sc->bif.type, ACPI_CMBAT_MAXSTRLEN) != 0) 359 goto end; 360 if (acpi_PkgStr(res, 12, sc->bif.oeminfo, ACPI_CMBAT_MAXSTRLEN) != 0) 361 goto end; 362 363end: 364 if (bif_buffer.Pointer != NULL) 365 AcpiOsFree(bif_buffer.Pointer); 366} 367 368static int 369acpi_cmbat_bif(device_t dev, struct acpi_bif *bifp) 370{ 371 struct acpi_cmbat_softc *sc; 372 373 sc = device_get_softc(dev); 374 375 /* 376 * Just copy the data. The only value that should change is the 377 * last-full capacity, so we only update when we get a notify that says 378 * the info has changed. Many systems apparently take a long time to 379 * process a _BIF call so we avoid it if possible. 380 */ 381 ACPI_SERIAL_BEGIN(cmbat); 382 bifp->units = sc->bif.units; 383 bifp->dcap = sc->bif.dcap; 384 bifp->lfcap = sc->bif.lfcap; 385 bifp->btech = sc->bif.btech; 386 bifp->dvol = sc->bif.dvol; 387 bifp->wcap = sc->bif.wcap; 388 bifp->lcap = sc->bif.lcap; 389 bifp->gra1 = sc->bif.gra1; 390 bifp->gra2 = sc->bif.gra2; 391 strncpy(bifp->model, sc->bif.model, sizeof(sc->bif.model)); 392 strncpy(bifp->serial, sc->bif.serial, sizeof(sc->bif.serial)); 393 strncpy(bifp->type, sc->bif.type, sizeof(sc->bif.type)); 394 strncpy(bifp->oeminfo, sc->bif.oeminfo, sizeof(sc->bif.oeminfo)); 395 ACPI_SERIAL_END(cmbat); 396 397 return (0); 398} 399 400static int 401acpi_cmbat_bst(device_t dev, struct acpi_bst *bstp) 402{ 403 struct acpi_cmbat_softc *sc; 404 405 sc = device_get_softc(dev); 406 407 ACPI_SERIAL_BEGIN(cmbat); 408 if (acpi_BatteryIsPresent(dev)) { 409 acpi_cmbat_get_bst(dev); 410 bstp->state = sc->bst.state; 411 bstp->rate = sc->bst.rate; 412 bstp->cap = sc->bst.cap; 413 bstp->volt = sc->bst.volt; 414 } else 415 bstp->state = ACPI_BATT_STAT_NOT_PRESENT; 416 ACPI_SERIAL_END(cmbat); 417 418 return (0); 419} 420 421static void 422acpi_cmbat_init_battery(void *arg) 423{ 424 struct acpi_cmbat_softc *sc; 425 int retry, valid; 426 device_t dev; 427 428 dev = (device_t)arg; 429 sc = device_get_softc(dev); 430 ACPI_VPRINT(dev, acpi_device_get_parent_softc(dev), 431 "battery initialization start\n"); 432 433 /* 434 * Try repeatedly to get valid data from the battery. Since the 435 * embedded controller isn't always ready just after boot, we may have 436 * to wait a while. 437 */ 438 for (retry = 0; retry < ACPI_CMBAT_RETRY_MAX; retry++, AcpiOsSleep(10000)) {
|
| 439 /* batteries on DOCK can be ejected w/ DOCK during retrying */ 440 if (!device_is_attached(dev)) 441 return; 442
|
438 if (!acpi_BatteryIsPresent(dev)) 439 continue; 440 441 /* 442 * Only query the battery if this is the first try or the specific 443 * type of info is still invalid. 444 */ 445 ACPI_SERIAL_BEGIN(cmbat); 446 if (retry == 0 || !acpi_battery_bst_valid(&sc->bst)) { 447 timespecclear(&sc->bst_lastupdated); 448 acpi_cmbat_get_bst(dev); 449 } 450 if (retry == 0 || !acpi_battery_bif_valid(&sc->bif)) 451 acpi_cmbat_get_bif(dev); 452 453 valid = acpi_battery_bst_valid(&sc->bst) && 454 acpi_battery_bif_valid(&sc->bif); 455 ACPI_SERIAL_END(cmbat); 456 457 if (valid) 458 break; 459 } 460 461 if (retry == ACPI_CMBAT_RETRY_MAX) { 462 ACPI_VPRINT(dev, acpi_device_get_parent_softc(dev), 463 "battery initialization failed, giving up\n"); 464 } else { 465 ACPI_VPRINT(dev, acpi_device_get_parent_softc(dev), 466 "battery initialization done, tried %d times\n", retry + 1); 467 } 468}
| 443 if (!acpi_BatteryIsPresent(dev)) 444 continue; 445 446 /* 447 * Only query the battery if this is the first try or the specific 448 * type of info is still invalid. 449 */ 450 ACPI_SERIAL_BEGIN(cmbat); 451 if (retry == 0 || !acpi_battery_bst_valid(&sc->bst)) { 452 timespecclear(&sc->bst_lastupdated); 453 acpi_cmbat_get_bst(dev); 454 } 455 if (retry == 0 || !acpi_battery_bif_valid(&sc->bif)) 456 acpi_cmbat_get_bif(dev); 457 458 valid = acpi_battery_bst_valid(&sc->bst) && 459 acpi_battery_bif_valid(&sc->bif); 460 ACPI_SERIAL_END(cmbat); 461 462 if (valid) 463 break; 464 } 465 466 if (retry == ACPI_CMBAT_RETRY_MAX) { 467 ACPI_VPRINT(dev, acpi_device_get_parent_softc(dev), 468 "battery initialization failed, giving up\n"); 469 } else { 470 ACPI_VPRINT(dev, acpi_device_get_parent_softc(dev), 471 "battery initialization done, tried %d times\n", retry + 1); 472 } 473}
|