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: releng/5.2/sys/dev/acpica/acpi_cmbat.c 121493 2003-10-25 05:03:25Z njl $
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 acpi_UserNotify("CMBAT", h, notify);
299
300 switch (notify) {
301 case ACPI_NOTIFY_DEVICE_CHECK:
302 case ACPI_BATTERY_BST_CHANGE:
303 timespecclear(&sc->bst_lastupdated);
304 break;
305 case ACPI_NOTIFY_BUS_CHECK:
306 case ACPI_BATTERY_BIF_CHANGE:
307 timespecclear(&sc->bif_lastupdated);
308 AcpiOsQueueForExecution(OSD_PRIORITY_LO, acpi_cmbat_get_bif, dev);
309 break;
310 default:
311 break;
312 }
313 }
314
315 static int
316 acpi_cmbat_probe(device_t dev)
317 {
318 if (acpi_get_type(dev) == ACPI_TYPE_DEVICE &&
319 !acpi_disabled("cmbat") && acpi_MatchHid(dev, "PNP0C0A")) {
320
321 device_set_desc(dev, "Control Method Battery");
322 return (0);
323 }
324 return (ENXIO);
325 }
326
327 static int
328 acpi_cmbat_attach(device_t dev)
329 {
330 int error;
331 ACPI_HANDLE handle;
332 struct acpi_cmbat_softc *sc;
333
334 if ((sc = device_get_softc(dev)) == NULL)
335 return (ENXIO);
336
337 handle = acpi_get_handle(dev);
338
339 /*
340 * Install a system notify handler in addition to the device notify.
341 * Toshiba notebook uses this alternate notify for its battery.
342 */
343 AcpiInstallNotifyHandler(handle, ACPI_SYSTEM_NOTIFY,
344 acpi_cmbat_notify_handler, dev);
345 AcpiInstallNotifyHandler(handle, ACPI_DEVICE_NOTIFY,
346 acpi_cmbat_notify_handler, dev);
347
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 error = acpi_register_ioctl(ACPIIO_CMBAT_GET_BIF,
356 acpi_cmbat_ioctl, NULL);
357 if (error != 0)
358 return (error);
359 error = acpi_register_ioctl(ACPIIO_CMBAT_GET_BST,
360 acpi_cmbat_ioctl, NULL);
361 if (error != 0)
362 return (error);
363 }
364
365 error = acpi_battery_register(ACPI_BATT_TYPE_CMBAT, acpi_cmbat_units);
366 if (error != 0)
367 return (error);
368
369 acpi_cmbat_units++;
370 timespecclear(&acpi_cmbat_info_lastupdated);
371 sc->initializing = 0;
372 AcpiOsQueueForExecution(OSD_PRIORITY_LO, acpi_cmbat_init_battery, dev);
373
374 return (0);
375 }
376
377 static int
378 acpi_cmbat_resume(device_t dev)
379 {
380 AcpiOsQueueForExecution(OSD_PRIORITY_LO, acpi_cmbat_init_battery, dev);
381 return (0);
382 }
383
384 static int
385 acpi_cmbat_ioctl(u_long cmd, caddr_t addr, void *arg)
386 {
387 device_t dev;
388 union acpi_battery_ioctl_arg *ioctl_arg;
389 struct acpi_cmbat_softc *sc;
390 struct acpi_bif *bifp;
391 struct acpi_bst *bstp;
392
393 ioctl_arg = (union acpi_battery_ioctl_arg *)addr;
394 dev = devclass_get_device(acpi_cmbat_devclass, ioctl_arg->unit);
395 if (dev == NULL)
396 return (ENXIO);
397 sc = device_get_softc(dev);
398 if (sc == NULL)
399 return (ENXIO);
400
401 /*
402 * No security check required: information retrieval only. If
403 * new functions are added here, a check might be required.
404 */
405 switch (cmd) {
406 case ACPIIO_CMBAT_GET_BIF:
407 acpi_cmbat_get_bif(dev);
408 bifp = &ioctl_arg->bif;
409 bifp->units = sc->bif.units;
410 bifp->dcap = sc->bif.dcap;
411 bifp->lfcap = sc->bif.lfcap;
412 bifp->btech = sc->bif.btech;
413 bifp->dvol = sc->bif.dvol;
414 bifp->wcap = sc->bif.wcap;
415 bifp->lcap = sc->bif.lcap;
416 bifp->gra1 = sc->bif.gra1;
417 bifp->gra2 = sc->bif.gra2;
418 strncpy(bifp->model, sc->bif.model, sizeof(sc->bif.model));
419 strncpy(bifp->serial, sc->bif.serial, sizeof(sc->bif.serial));
420 strncpy(bifp->type, sc->bif.type, sizeof(sc->bif.type));
421 strncpy(bifp->oeminfo, sc->bif.oeminfo, sizeof(sc->bif.oeminfo));
422 break;
423 case ACPIIO_CMBAT_GET_BST:
424 bstp = &ioctl_arg->bst;
425 if (acpi_BatteryIsPresent(dev)) {
426 acpi_cmbat_get_bst(dev);
427 bstp->state = sc->bst.state;
428 bstp->rate = sc->bst.rate;
429 bstp->cap = sc->bst.cap;
430 bstp->volt = sc->bst.volt;
431 } else {
432 bstp->state = ACPI_BATT_STAT_NOT_PRESENT;
433 }
434 break;
435 default:
436 break;
437 }
438
439 return (0);
440 }
441
442 static int
443 acpi_cmbat_is_bst_valid(struct acpi_bst *bst)
444 {
445 if (bst->state >= ACPI_BATT_STAT_MAX || bst->cap == 0xffffffff ||
446 bst->volt == 0xffffffff)
447
448 return (0);
449 else
450 return (1);
451 }
452
453 static int
454 acpi_cmbat_is_bif_valid(struct acpi_bif *bif)
455 {
456 if (bif->lfcap == 0)
457 return (0);
458 else
459 return (1);
460 }
461
462 static int
463 acpi_cmbat_get_total_battinfo(struct acpi_battinfo *battinfo)
464 {
465 int i;
466 int error;
467 int batt_stat;
468 int valid_rate, valid_units;
469 int cap, min;
470 int total_cap, total_min, total_full;
471 device_t dev;
472 struct acpi_cmbat_softc *sc;
473 static int bat_units = 0;
474 static struct acpi_cmbat_softc **bat = NULL;
475
476 cap = min = -1;
477 batt_stat = ACPI_BATT_STAT_NOT_PRESENT;
478 error = 0;
479
480 /* Allocate array of softc pointers */
481 if (bat_units != acpi_cmbat_units) {
482 if (bat != NULL) {
483 free(bat, M_ACPICMBAT);
484 bat = NULL;
485 }
486 bat_units = 0;
487 }
488 if (bat == NULL) {
489 bat_units = acpi_cmbat_units;
490 bat = malloc(sizeof(struct acpi_cmbat_softc *) * bat_units,
491 M_ACPICMBAT, M_NOWAIT);
492 if (bat == NULL) {
493 error = ENOMEM;
494 goto out;
495 }
496
497 /* Collect softc pointers */
498 for (i = 0; i < acpi_cmbat_units; i++) {
499 if ((dev = devclass_get_device(acpi_cmbat_devclass, i)) == NULL) {
500 error = ENXIO;
501 goto out;
502 }
503 if ((sc = device_get_softc(dev)) == NULL) {
504 error = ENXIO;
505 goto out;
506 }
507 bat[i] = sc;
508 }
509 }
510
511 /* Get battery status, valid rate and valid units */
512 batt_stat = valid_rate = valid_units = 0;
513 for (i = 0; i < acpi_cmbat_units; i++) {
514 bat[i]->present = acpi_BatteryIsPresent(bat[i]->dev);
515 if (!bat[i]->present)
516 continue;
517
518 acpi_cmbat_get_bst(bat[i]->dev);
519
520 /* If battery not installed, we get strange values */
521 if (!acpi_cmbat_is_bst_valid(&(bat[i]->bst)) ||
522 !acpi_cmbat_is_bif_valid(&(bat[i]->bif))) {
523
524 bat[i]->present = 0;
525 continue;
526 }
527
528 valid_units++;
529 bat[i]->cap = 100 * bat[i]->bst.cap / bat[i]->bif.lfcap;
530 batt_stat |= bat[i]->bst.state;
531
532 if (bat[i]->bst.rate > 0) {
533 /*
534 * XXX Hack to calculate total battery time.
535 * Systems with 2 or more battries, they may get used
536 * one by one, thus bst.rate is set only to the one
537 * in use. For remaining batteries bst.rate = 0, which
538 * makes it impossible to calculate remaining time.
539 * Some other systems may need sum of bst.rate in
540 * dis-charging state.
541 * There for we sum up the bst.rate that is valid
542 * (in dis-charging state), and use the sum to
543 * calcutate remaining batteries' time.
544 */
545 if (bat[i]->bst.state & ACPI_BATT_STAT_DISCHARG)
546 valid_rate += bat[i]->bst.rate;
547 }
548 }
549
550 /* Calculate total battery capacity and time */
551 total_cap = total_min = total_full = 0;
552 for (i = 0; i < acpi_cmbat_units; i++) {
553 if (!bat[i]->present)
554 continue;
555
556 if (valid_rate > 0) {
557 /* Use the sum of bst.rate */
558 bat[i]->min = 60 * bat[i]->bst.cap / valid_rate;
559 } else if (bat[i]->full_charge_time > 0) {
560 bat[i]->min = (bat[i]->full_charge_time * bat[i]->cap) / 100;
561 } else {
562 /* Couldn't find valid rate and full battery time */
563 bat[i]->min = 0;
564 }
565 total_min += bat[i]->min;
566 total_cap += bat[i]->cap;
567 total_full += bat[i]->full_charge_time;
568 }
569
570 /* Battery life */
571 if (valid_units == 0) {
572 cap = -1;
573 batt_stat = ACPI_BATT_STAT_NOT_PRESENT;
574 } else {
575 cap = total_cap / valid_units;
576 }
577
578 /* Battery time */
579 if (valid_units == 0) {
580 min = -1;
581 } else if (valid_rate == 0 || (batt_stat & ACPI_BATT_STAT_CHARGING)) {
582 if (total_full == 0)
583 min = -1;
584 else
585 min = (total_full * cap) / 100;
586 } else {
587 min = total_min;
588 }
589 acpi_cmbat_info_updated(&acpi_cmbat_info_lastupdated);
590
591 out:
592 battinfo->cap = cap;
593 battinfo->min = min;
594 battinfo->state = batt_stat;
595
596 return (error);
597 }
598
599 static void
600 acpi_cmbat_init_battery(void *arg)
601 {
602 int retry;
603 device_t dev = (device_t)arg;
604 struct acpi_cmbat_softc *sc = device_get_softc(dev);
605
606 if (sc->initializing)
607 return;
608
609 sc->initializing = 1;
610 ACPI_VPRINT(dev, acpi_device_get_parent_softc(dev),
611 "battery initialization start\n");
612
613 for (retry = 0; retry < ACPI_CMBAT_RETRY_MAX; retry++, AcpiOsSleep(10, 0)) {
614 sc->present = acpi_BatteryIsPresent(dev);
615 if (!sc->present)
616 continue;
617
618 timespecclear(&sc->bst_lastupdated);
619 timespecclear(&sc->bif_lastupdated);
620
621 acpi_cmbat_get_bst(dev);
622 if (!acpi_cmbat_is_bst_valid(&sc->bst))
623 continue;
624
625 acpi_cmbat_get_bif(dev);
626 if (!acpi_cmbat_is_bif_valid(&sc->bif))
627 continue;
628 break;
629 }
630
631 sc->initializing = 0;
632 if (retry == ACPI_CMBAT_RETRY_MAX) {
633 ACPI_VPRINT(dev, acpi_device_get_parent_softc(dev),
634 "battery initialization failed, giving up\n");
635 } else {
636 ACPI_VPRINT(dev, acpi_device_get_parent_softc(dev),
637 "battery initialization done, tried %d times\n", retry + 1);
638 }
639 }
640
641 /*
642 * Public interfaces.
643 */
644 int
645 acpi_cmbat_get_battinfo(int unit, struct acpi_battinfo *battinfo)
646 {
647 int error;
648 device_t dev;
649 struct acpi_cmbat_softc *sc;
650
651 if (unit == -1)
652 return (acpi_cmbat_get_total_battinfo(battinfo));
653
654 if (acpi_cmbat_info_expired(&acpi_cmbat_info_lastupdated)) {
655 error = acpi_cmbat_get_total_battinfo(battinfo);
656 if (error)
657 goto out;
658 }
659
660 error = 0;
661 if (unit >= acpi_cmbat_units) {
662 error = ENXIO;
663 goto out;
664 }
665
666 if ((dev = devclass_get_device(acpi_cmbat_devclass, unit)) == NULL) {
667 error = ENXIO;
668 goto out;
669 }
670 if ((sc = device_get_softc(dev)) == NULL) {
671 error = ENXIO;
672 goto out;
673 }
674
675 if (!sc->present) {
676 battinfo->cap = -1;
677 battinfo->min = -1;
678 battinfo->state = ACPI_BATT_STAT_NOT_PRESENT;
679 } else {
680 battinfo->cap = sc->cap;
681 battinfo->min = sc->min;
682 battinfo->state = sc->bst.state;
683 }
684
685 out:
686 return (error);
687 }
Cache object: c64c760dc7548e0f5a21a5c7ec0a8e39
|