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