xref: /netbsd-src/sys/dev/acpi/acpi_bat.c (revision 7fa608457b817eca6e0977b37f758ae064f3c99c)
1 /*	$NetBSD: acpi_bat.c,v 1.61 2007/11/03 23:05:22 xtraeme Exp $	*/
2 
3 /*-
4  * Copyright (c) 2003 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to The NetBSD Foundation
8  * by Charles M. Hannum of By Noon Software, Inc.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  * 3. All advertising materials mentioning features or use of this software
19  *    must display the following acknowledgement:
20  *	This product includes software developed by the NetBSD
21  *	Foundation, Inc. and its contributors.
22  * 4. Neither the name of The NetBSD Foundation nor the names of its
23  *    contributors may be used to endorse or promote products derived
24  *    from this software without specific prior written permission.
25  *
26  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
27  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
28  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
30  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36  * POSSIBILITY OF SUCH DAMAGE.
37  */
38 
39 /*
40  * Copyright 2001 Bill Sommerfeld.
41  * All rights reserved.
42  *
43  * Redistribution and use in source and binary forms, with or without
44  * modification, are permitted provided that the following conditions
45  * are met:
46  * 1. Redistributions of source code must retain the above copyright
47  *    notice, this list of conditions and the following disclaimer.
48  * 2. Redistributions in binary form must reproduce the above copyright
49  *    notice, this list of conditions and the following disclaimer in the
50  *    documentation and/or other materials provided with the distribution.
51  * 3. All advertising materials mentioning features or use of this software
52  *    must display the following acknowledgement:
53  *	This product includes software developed for the NetBSD Project by
54  *	Wasabi Systems, Inc.
55  * 4. The name of Wasabi Systems, Inc. may not be used to endorse
56  *    or promote products derived from this software without specific prior
57  *    written permission.
58  *
59  * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND
60  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
61  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
62  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL WASABI SYSTEMS, INC
63  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
64  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
65  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
66  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
67  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
68  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
69  * POSSIBILITY OF SUCH DAMAGE.
70  */
71 
72 #if 0
73 #define ACPI_BAT_DEBUG
74 #endif
75 
76 /*
77  * ACPI Battery Driver.
78  *
79  * ACPI defines two different battery device interfaces: "Control
80  * Method" batteries, in which AML methods are defined in order to get
81  * battery status and set battery alarm thresholds, and a "Smart
82  * Battery" device, which is an SMbus device accessed through the ACPI
83  * Embedded Controller device.
84  *
85  * This driver is for the "Control Method"-style battery only.
86  */
87 
88 #include <sys/cdefs.h>
89 __KERNEL_RCSID(0, "$NetBSD: acpi_bat.c,v 1.61 2007/11/03 23:05:22 xtraeme Exp $");
90 
91 #include <sys/param.h>
92 #include <sys/systm.h>
93 #include <sys/kernel.h>		/* for hz */
94 #include <sys/device.h>
95 #include <sys/mutex.h>
96 #include <dev/sysmon/sysmonvar.h>
97 
98 #include <dev/acpi/acpica.h>
99 #include <dev/acpi/acpireg.h>
100 #include <dev/acpi/acpivar.h>
101 
102 /* sensor indexes */
103 #define ACPIBAT_PRESENT		0
104 #define ACPIBAT_DCAPACITY	1
105 #define ACPIBAT_LFCCAPACITY	2
106 #define ACPIBAT_TECHNOLOGY	3
107 #define ACPIBAT_DVOLTAGE	4
108 #define ACPIBAT_WCAPACITY	5
109 #define ACPIBAT_LCAPACITY	6
110 #define ACPIBAT_VOLTAGE		7
111 #define ACPIBAT_CHARGERATE	8
112 #define ACPIBAT_DISCHARGERATE	9
113 #define ACPIBAT_CAPACITY	10
114 #define ACPIBAT_CHARGING	11
115 #define ACPIBAT_CHARGE_STATE	12
116 #define ACPIBAT_NSENSORS	13  /* number of sensors */
117 
118 struct acpibat_softc {
119 	struct acpi_devnode *sc_node;	/* our ACPI devnode */
120 	int sc_flags;			/* see below */
121 	int sc_available;		/* available information level */
122 
123 	struct sysmon_envsys sc_sysmon;
124 	struct envsys_data sc_data[ACPIBAT_NSENSORS];
125 	kmutex_t sc_mtx;
126 
127 	struct timeval sc_lastupdate, sc_updateinterval;
128 };
129 
130 static const char * const bat_hid[] = {
131 	"PNP0C0A",
132 	NULL
133 };
134 
135 /*
136  * These flags are used to examine the battery device data returned from
137  * the ACPI interface, specifically the "battery status"
138  */
139 #define ACPIBAT_PWRUNIT_MA	0x00000001  /* mA not mW */
140 
141 /*
142  * These flags are used to examine the battery charge/discharge/critical
143  * state returned from a get-status command.
144  */
145 #define ACPIBAT_ST_DISCHARGING	0x00000001  /* battery is discharging */
146 #define ACPIBAT_ST_CHARGING	0x00000002  /* battery is charging */
147 #define ACPIBAT_ST_CRITICAL	0x00000004  /* battery is critical */
148 
149 /*
150  * Flags for battery status from _STA return
151  */
152 #define ACPIBAT_STA_PRESENT	0x00000010  /* battery present */
153 
154 /*
155  * These flags are used to set internal state in our softc.
156  */
157 #define	ABAT_F_VERBOSE		0x01	/* verbose events */
158 #define ABAT_F_PWRUNIT_MA	0x02 	/* mA instead of mW */
159 #define ABAT_F_PRESENT		0x04	/* is the battery present? */
160 
161 #define ABAT_SET(sc, f)		(void)((sc)->sc_flags |= (f))
162 #define ABAT_CLEAR(sc, f)	(void)((sc)->sc_flags &= ~(f))
163 #define ABAT_ISSET(sc, f)	((sc)->sc_flags & (f))
164 
165 /*
166  * Available info level
167  */
168 
169 #define ABAT_ALV_NONE		0	/* none is available */
170 #define ABAT_ALV_PRESENCE	1	/* presence info is available */
171 #define ABAT_ALV_INFO		2	/* battery info is available */
172 #define ABAT_ALV_STAT		3	/* battery status is available */
173 
174 static int	acpibat_match(device_t, struct cfdata *, void *);
175 static void	acpibat_attach(device_t, struct device *, void *);
176 
177 CFATTACH_DECL_NEW(acpibat, sizeof(struct acpibat_softc),
178     acpibat_match, acpibat_attach, NULL, NULL);
179 
180 static void acpibat_clear_presence(struct acpibat_softc *);
181 static void acpibat_clear_info(struct acpibat_softc *);
182 static void acpibat_clear_stat(struct acpibat_softc *);
183 static int acpibat_battery_present(device_t);
184 static ACPI_STATUS acpibat_get_status(device_t);
185 static ACPI_STATUS acpibat_get_info(device_t);
186 static void acpibat_print_info(device_t);
187 static void acpibat_print_stat(device_t);
188 static void acpibat_update(void *);
189 
190 static void acpibat_init_envsys(device_t);
191 static void acpibat_notify_handler(ACPI_HANDLE, UINT32, void *);
192 static int acpibat_gtredata(struct sysmon_envsys *, envsys_data_t *);
193 
194 /*
195  * acpibat_match:
196  *
197  *	Autoconfiguration `match' routine.
198  */
199 static int
200 acpibat_match(device_t parent, struct cfdata *match, void *aux)
201 {
202 	struct acpi_attach_args *aa = aux;
203 
204 	if (aa->aa_node->ad_type != ACPI_TYPE_DEVICE)
205 		return 0;
206 
207 	return acpi_match_hid(aa->aa_node->ad_devinfo, bat_hid);
208 }
209 
210 /*
211  * acpibat_attach:
212  *
213  *	Autoconfiguration `attach' routine.
214  */
215 static void
216 acpibat_attach(device_t parent, device_t self, void *aux)
217 {
218 	struct acpibat_softc *sc = device_private(self);
219 	struct acpi_attach_args *aa = aux;
220 	ACPI_STATUS rv;
221 
222 	aprint_naive(": ACPI Battery (Control Method)\n");
223 	aprint_normal(": ACPI Battery (Control Method)\n");
224 
225 	sc->sc_node = aa->aa_node;
226 	mutex_init(&sc->sc_mtx, MUTEX_DRIVER, IPL_NONE);
227 
228 	rv = AcpiInstallNotifyHandler(sc->sc_node->ad_handle,
229 				      ACPI_ALL_NOTIFY,
230 				      acpibat_notify_handler, self);
231 	if (ACPI_FAILURE(rv)) {
232 		aprint_error_dev(self,
233 		    "unable to register DEVICE/SYSTEM NOTIFY handler: %s\n",
234 		    AcpiFormatException(rv));
235 		return;
236 	}
237 
238 #ifdef ACPI_BAT_DEBUG
239 	ABAT_SET(sc, ABAT_F_VERBOSE);
240 #endif
241 
242 	acpibat_init_envsys(self);
243 }
244 
245 /*
246  * clear informations
247  */
248 
249 static void
250 acpibat_clear_presence(struct acpibat_softc *sc)
251 {
252 	acpibat_clear_info(sc);
253 	sc->sc_available = ABAT_ALV_NONE;
254 	ABAT_CLEAR(sc, ABAT_F_PRESENT);
255 }
256 
257 static void
258 acpibat_clear_info(struct acpibat_softc *sc)
259 {
260 	acpibat_clear_stat(sc);
261 	if (sc->sc_available > ABAT_ALV_PRESENCE)
262 		sc->sc_available = ABAT_ALV_PRESENCE;
263 
264 	sc->sc_data[ACPIBAT_DCAPACITY].state = ENVSYS_SINVALID;
265 	sc->sc_data[ACPIBAT_LFCCAPACITY].state = ENVSYS_SINVALID;
266 	sc->sc_data[ACPIBAT_CAPACITY].state = ENVSYS_SINVALID;
267 	sc->sc_data[ACPIBAT_TECHNOLOGY].state = ENVSYS_SINVALID;
268 	sc->sc_data[ACPIBAT_DVOLTAGE].state = ENVSYS_SINVALID;
269 	sc->sc_data[ACPIBAT_WCAPACITY].state = ENVSYS_SINVALID;
270 	sc->sc_data[ACPIBAT_LCAPACITY].state = ENVSYS_SINVALID;
271 }
272 
273 static void
274 acpibat_clear_stat(struct acpibat_softc *sc)
275 {
276 	if (sc->sc_available > ABAT_ALV_INFO)
277 		sc->sc_available = ABAT_ALV_INFO;
278 
279 	sc->sc_data[ACPIBAT_CHARGERATE].state = ENVSYS_SINVALID;
280 	sc->sc_data[ACPIBAT_DISCHARGERATE].state = ENVSYS_SINVALID;
281 	sc->sc_data[ACPIBAT_CAPACITY].state = ENVSYS_SINVALID;
282 	sc->sc_data[ACPIBAT_VOLTAGE].state = ENVSYS_SINVALID;
283 	sc->sc_data[ACPIBAT_CHARGING].state = ENVSYS_SINVALID;
284 }
285 
286 
287 /*
288  * returns 0 for no battery, 1 for present, and -1 on error
289  */
290 static int
291 acpibat_battery_present(device_t dv)
292 {
293 	struct acpibat_softc *sc = device_private(dv);
294 	uint32_t sta;
295 	ACPI_INTEGER val;
296 	ACPI_STATUS rv;
297 
298 	rv = acpi_eval_integer(sc->sc_node->ad_handle, "_STA", &val);
299 	if (ACPI_FAILURE(rv)) {
300 		aprint_error_dev(dv, "failed to evaluate _STA: %s\n",
301 		    AcpiFormatException(rv));
302 		return -1;
303 	}
304 
305 	sta = (uint32_t)val;
306 
307 	mutex_enter(&sc->sc_mtx);
308 	sc->sc_available = ABAT_ALV_PRESENCE;
309 	if (sta & ACPIBAT_STA_PRESENT) {
310 		ABAT_SET(sc, ABAT_F_PRESENT);
311 		sc->sc_data[ACPIBAT_PRESENT].state = ENVSYS_SVALID;
312 		sc->sc_data[ACPIBAT_PRESENT].value_cur = 1;
313 	} else
314 		sc->sc_data[ACPIBAT_PRESENT].value_cur = 0;
315 
316 	mutex_exit(&sc->sc_mtx);
317 
318 	return (sta & ACPIBAT_STA_PRESENT) ? 1 : 0;
319 }
320 
321 /*
322  * acpibat_get_info
323  *
324  * 	Get, and possibly display, the battery info.
325  */
326 
327 static ACPI_STATUS
328 acpibat_get_info(device_t dv)
329 {
330 	struct acpibat_softc *sc = device_private(dv);
331 	ACPI_OBJECT *p1, *p2;
332 	ACPI_STATUS rv;
333 	ACPI_BUFFER buf;
334 	int capunit, rateunit;
335 
336 	rv = acpi_eval_struct(sc->sc_node->ad_handle, "_BIF", &buf);
337 	if (ACPI_FAILURE(rv)) {
338 		aprint_error_dev(dv, "failed to evaluate _BIF: %s\n",
339 		    AcpiFormatException(rv));
340 		return rv;
341 	}
342 	p1 = (ACPI_OBJECT *)buf.Pointer;
343 
344 	if (p1->Type != ACPI_TYPE_PACKAGE) {
345 		aprint_error_dev(dv, "expected PACKAGE, got %d\n", p1->Type);
346 		goto out;
347 	}
348 	if (p1->Package.Count < 13) {
349 		aprint_error_dev(dv, "expected 13 elements, got %d\n",
350 		    p1->Package.Count);
351 		goto out;
352 	}
353 	p2 = p1->Package.Elements;
354 
355 	mutex_enter(&sc->sc_mtx);
356 	if ((p2[0].Integer.Value & ACPIBAT_PWRUNIT_MA) != 0) {
357 		ABAT_SET(sc, ABAT_F_PWRUNIT_MA);
358 		capunit = ENVSYS_SAMPHOUR;
359 		rateunit = ENVSYS_SAMPS;
360 	} else {
361 		ABAT_CLEAR(sc, ABAT_F_PWRUNIT_MA);
362 		capunit = ENVSYS_SWATTHOUR;
363 		rateunit = ENVSYS_SWATTS;
364 	}
365 
366 	sc->sc_data[ACPIBAT_DCAPACITY].units = capunit;
367 	sc->sc_data[ACPIBAT_LFCCAPACITY].units = capunit;
368 	sc->sc_data[ACPIBAT_WCAPACITY].units = capunit;
369 	sc->sc_data[ACPIBAT_LCAPACITY].units = capunit;
370 	sc->sc_data[ACPIBAT_CHARGERATE].units = rateunit;
371 	sc->sc_data[ACPIBAT_DISCHARGERATE].units = rateunit;
372 	sc->sc_data[ACPIBAT_CAPACITY].units = capunit;
373 
374 	sc->sc_data[ACPIBAT_DCAPACITY].value_cur = p2[1].Integer.Value * 1000;
375 	sc->sc_data[ACPIBAT_DCAPACITY].state = ENVSYS_SVALID;
376 	sc->sc_data[ACPIBAT_LFCCAPACITY].value_cur = p2[2].Integer.Value * 1000;
377 	sc->sc_data[ACPIBAT_LFCCAPACITY].state = ENVSYS_SVALID;
378 	sc->sc_data[ACPIBAT_CAPACITY].value_max = p2[2].Integer.Value * 1000;
379 	sc->sc_data[ACPIBAT_TECHNOLOGY].value_cur = p2[3].Integer.Value;
380 	sc->sc_data[ACPIBAT_TECHNOLOGY].state = ENVSYS_SVALID;
381 	sc->sc_data[ACPIBAT_DVOLTAGE].value_cur = p2[4].Integer.Value * 1000;
382 	sc->sc_data[ACPIBAT_DVOLTAGE].state = ENVSYS_SVALID;
383 	sc->sc_data[ACPIBAT_WCAPACITY].value_cur = p2[5].Integer.Value * 1000;
384 	sc->sc_data[ACPIBAT_WCAPACITY].value_max = p2[2].Integer.Value * 1000;
385 	sc->sc_data[ACPIBAT_WCAPACITY].state = ENVSYS_SVALID;
386 	sc->sc_data[ACPIBAT_WCAPACITY].flags |=
387 	    (ENVSYS_FPERCENT|ENVSYS_FVALID_MAX);
388 	sc->sc_data[ACPIBAT_LCAPACITY].value_cur = p2[6].Integer.Value * 1000;
389 	sc->sc_data[ACPIBAT_LCAPACITY].value_max = p2[2].Integer.Value * 1000;
390 	sc->sc_data[ACPIBAT_LCAPACITY].state = ENVSYS_SVALID;
391 	sc->sc_data[ACPIBAT_LCAPACITY].flags |=
392 	    (ENVSYS_FPERCENT|ENVSYS_FVALID_MAX);
393 	sc->sc_available = ABAT_ALV_INFO;
394 
395 	mutex_exit(&sc->sc_mtx);
396 
397 	aprint_verbose_dev(dv, "battery info: %s, %s, %s",
398 	    p2[12].String.Pointer, p2[11].String.Pointer, p2[9].String.Pointer);
399 	if (p2[10].String.Pointer)
400 		aprint_verbose(" %s", p2[10].String.Pointer);
401 
402 	aprint_verbose("\n");
403 
404 	rv = AE_OK;
405 
406 out:
407 	AcpiOsFree(buf.Pointer);
408 	return rv;
409 }
410 
411 /*
412  * acpibat_get_status:
413  *
414  *	Get, and possibly display, the current battery line status.
415  */
416 static ACPI_STATUS
417 acpibat_get_status(device_t dv)
418 {
419 	struct acpibat_softc *sc = device_private(dv);
420 	int status, battrate;
421 	ACPI_OBJECT *p1, *p2;
422 	ACPI_STATUS rv;
423 	ACPI_BUFFER buf;
424 
425 	rv = acpi_eval_struct(sc->sc_node->ad_handle, "_BST", &buf);
426 	if (ACPI_FAILURE(rv)) {
427 		aprint_error_dev(dv, "failed to evaluate _BST: %s\n",
428 		    AcpiFormatException(rv));
429 		return rv;
430 	}
431 	p1 = (ACPI_OBJECT *)buf.Pointer;
432 
433 	if (p1->Type != ACPI_TYPE_PACKAGE) {
434 		aprint_error_dev(dv, "expected PACKAGE, got %d\n",
435 		    p1->Type);
436 		rv = AE_ERROR;
437 		goto out;
438 	}
439 	if (p1->Package.Count < 4) {
440 		aprint_error_dev(dv, "expected 4 elts, got %d\n",
441 		    p1->Package.Count);
442 		rv = AE_ERROR;
443 		goto out;
444 	}
445 	p2 = p1->Package.Elements;
446 
447 	mutex_enter(&sc->sc_mtx);
448 
449 	status = p2[0].Integer.Value;
450 	battrate = p2[1].Integer.Value;
451 
452 	if (status & ACPIBAT_ST_CHARGING) {
453 		sc->sc_data[ACPIBAT_CHARGERATE].state = ENVSYS_SVALID;
454 		sc->sc_data[ACPIBAT_CHARGERATE].value_cur = battrate * 1000;
455 		sc->sc_data[ACPIBAT_DISCHARGERATE].state = ENVSYS_SINVALID;
456 		sc->sc_data[ACPIBAT_CHARGING].state = ENVSYS_SVALID;
457 		sc->sc_data[ACPIBAT_CHARGING].value_cur = 1;
458 	} else if (status & ACPIBAT_ST_DISCHARGING) {
459 		sc->sc_data[ACPIBAT_DISCHARGERATE].state = ENVSYS_SVALID;
460 		sc->sc_data[ACPIBAT_DISCHARGERATE].value_cur = battrate * 1000;
461 		sc->sc_data[ACPIBAT_CHARGERATE].state = ENVSYS_SINVALID;
462 		sc->sc_data[ACPIBAT_CHARGING].state = ENVSYS_SVALID;
463 		sc->sc_data[ACPIBAT_CHARGING].value_cur = 0;
464 	} else if (!(status & (ACPIBAT_ST_CHARGING|ACPIBAT_ST_DISCHARGING))) {
465 		sc->sc_data[ACPIBAT_CHARGING].state = ENVSYS_SVALID;
466 		sc->sc_data[ACPIBAT_CHARGING].value_cur = 0;
467 		sc->sc_data[ACPIBAT_CHARGERATE].state = ENVSYS_SINVALID;
468 		sc->sc_data[ACPIBAT_DISCHARGERATE].state = ENVSYS_SINVALID;
469 	}
470 
471 	sc->sc_data[ACPIBAT_CHARGE_STATE].value_cur =
472 	    ENVSYS_BATTERY_CAPACITY_NORMAL;
473 
474 	sc->sc_data[ACPIBAT_CAPACITY].value_cur = p2[2].Integer.Value * 1000;
475 	sc->sc_data[ACPIBAT_CAPACITY].state = ENVSYS_SVALID;
476 	sc->sc_data[ACPIBAT_CAPACITY].flags |=
477 	    (ENVSYS_FPERCENT|ENVSYS_FVALID_MAX);
478 	sc->sc_data[ACPIBAT_VOLTAGE].value_cur = p2[3].Integer.Value * 1000;
479 	sc->sc_data[ACPIBAT_VOLTAGE].state = ENVSYS_SVALID;
480 
481 	if (sc->sc_data[ACPIBAT_CAPACITY].value_cur <
482 	    sc->sc_data[ACPIBAT_WCAPACITY].value_cur) {
483 		sc->sc_data[ACPIBAT_CAPACITY].state = ENVSYS_SWARNUNDER;
484 		sc->sc_data[ACPIBAT_CHARGE_STATE].value_cur =
485 		    ENVSYS_BATTERY_CAPACITY_WARNING;
486 	}
487 
488 	if (sc->sc_data[ACPIBAT_CAPACITY].value_cur <
489 	    sc->sc_data[ACPIBAT_LCAPACITY].value_cur) {
490 		sc->sc_data[ACPIBAT_CAPACITY].state = ENVSYS_SCRITUNDER;
491 		sc->sc_data[ACPIBAT_CHARGE_STATE].value_cur =
492 		    ENVSYS_BATTERY_CAPACITY_LOW;
493 	}
494 
495 	if (status & ACPIBAT_ST_CRITICAL) {
496 		sc->sc_data[ACPIBAT_CAPACITY].state = ENVSYS_SCRITICAL;
497 		sc->sc_data[ACPIBAT_CHARGE_STATE].value_cur =
498 		    ENVSYS_BATTERY_CAPACITY_CRITICAL;
499 	}
500 
501 	mutex_exit(&sc->sc_mtx);
502 
503 	rv = AE_OK;
504 
505 out:
506 	AcpiOsFree(buf.Pointer);
507 	return rv;
508 }
509 
510 #define SCALE(x)	((x)/1000000), (((x)%1000000)/1000)
511 #define CAPUNITS(sc)	(ABAT_ISSET((sc), ABAT_F_PWRUNIT_MA)?"Ah":"Wh")
512 #define RATEUNITS(sc)	(ABAT_ISSET((sc), ABAT_F_PWRUNIT_MA)?"A":"W")
513 static void
514 acpibat_print_info(device_t dv)
515 {
516 	struct acpibat_softc *sc = device_private(dv);
517 	const char *tech;
518 
519 	if (sc->sc_data[ACPIBAT_TECHNOLOGY].value_cur)
520 		tech = "secondary";
521 	else
522 		tech = "primary";
523 
524 	aprint_debug_dev(dv, "%s battery, Design %d.%03d%s "
525 	    "Last full %d.%03d%s Warn %d.%03d%s Low %d.%03d%s\n",
526 	    tech, SCALE(sc->sc_data[ACPIBAT_DCAPACITY].value_cur), CAPUNITS(sc),
527 	    SCALE(sc->sc_data[ACPIBAT_LFCCAPACITY].value_cur),CAPUNITS(sc),
528 	    SCALE(sc->sc_data[ACPIBAT_WCAPACITY].value_cur), CAPUNITS(sc),
529 	    SCALE(sc->sc_data[ACPIBAT_LCAPACITY].value_cur), CAPUNITS(sc));
530 }
531 
532 static void
533 acpibat_print_stat(device_t dv)
534 {
535 	struct acpibat_softc *sc = device_private(dv);
536 	const char *capstat, *chargestat;
537 	int percent, denom;
538 	int32_t value;
539 
540 	percent = 0;
541 
542 	if (sc->sc_data[ACPIBAT_CAPACITY].state == ENVSYS_SCRITUNDER)
543 		capstat = "CRITICAL UNDER ";
544 	else if (sc->sc_data[ACPIBAT_CAPACITY].state == ENVSYS_SCRITOVER)
545 		capstat = "CRITICAL OVER ";
546 	else
547 		capstat = "";
548 
549 	if (sc->sc_data[ACPIBAT_CHARGING].state != ENVSYS_SVALID) {
550 		chargestat = "idling";
551 		value = 0;
552 	} else if (sc->sc_data[ACPIBAT_CHARGING].value_cur == 0) {
553 		chargestat = "discharging";
554 		value = sc->sc_data[ACPIBAT_DISCHARGERATE].value_cur;
555 	} else {
556 		chargestat = "charging";
557 		value = sc->sc_data[ACPIBAT_CHARGERATE].value_cur;
558 	}
559 
560 	denom = sc->sc_data[ACPIBAT_LFCCAPACITY].value_cur / 100;
561 	if (denom > 0)
562 		percent = (sc->sc_data[ACPIBAT_CAPACITY].value_cur) / denom;
563 
564 	aprint_debug_dev(dv, "%s%s: %d.%03dV cap %d.%03d%s (%d%%) "
565 	    "rate %d.%03d%s\n", capstat, chargestat,
566 	    SCALE(sc->sc_data[ACPIBAT_VOLTAGE].value_cur),
567 	    SCALE(sc->sc_data[ACPIBAT_CAPACITY].value_cur), CAPUNITS(sc),
568 	    percent, SCALE(value), RATEUNITS(sc));
569 }
570 
571 static void
572 acpibat_update(void *arg)
573 {
574 	device_t dv = arg;
575 	struct acpibat_softc *sc = device_private(dv);
576 
577 	if (sc->sc_available < ABAT_ALV_INFO) {
578 		/* current information is invalid */
579 #if 0
580 		/*
581 		 * XXX: The driver sometimes unaware that the battery exist.
582 		 * (i.e. just after the boot or resuming)
583 		 * Thus, the driver should always check it here.
584 		 */
585 		if (sc->sc_available < ABAT_ALV_PRESENCE)
586 #endif
587 			/* presence is invalid */
588 			if (acpibat_battery_present(dv) < 0) {
589 				/* error */
590 				aprint_debug_dev(dv,
591 				    "cannot get battery presence.\n");
592 				return;
593 			}
594 
595 		if (ABAT_ISSET(sc, ABAT_F_PRESENT)) {
596 			/* the battery is present. */
597 			if (ABAT_ISSET(sc, ABAT_F_VERBOSE))
598 				aprint_debug_dev(dv,
599 				    "battery is present.\n");
600 			if (ACPI_FAILURE(acpibat_get_info(dv)))
601 				return;
602 			if (ABAT_ISSET(sc, ABAT_F_VERBOSE))
603 				acpibat_print_info(dv);
604 		} else {
605 			/* the battery is not present. */
606 			if (ABAT_ISSET(sc, ABAT_F_VERBOSE))
607 				aprint_debug_dev(dv,
608 				    "battery is not present.\n");
609 			return;
610 		}
611 	} else {
612 		/* current information is valid */
613 		if (!ABAT_ISSET(sc, ABAT_F_PRESENT)) {
614 			/* the battery is not present. */
615 			return;
616 		}
617  	}
618 
619 	if (ACPI_FAILURE(acpibat_get_status(dv)))
620 		return;
621 
622 	if (ABAT_ISSET(sc, ABAT_F_VERBOSE))
623 		acpibat_print_stat(dv);
624 }
625 
626 /*
627  * acpibat_notify_handler:
628  *
629  *	Callback from ACPI interrupt handler to notify us of an event.
630  */
631 static void
632 acpibat_notify_handler(ACPI_HANDLE handle, UINT32 notify, void *context)
633 {
634 	device_t dv = context;
635 	struct acpibat_softc *sc = device_private(dv);
636 	int rv;
637 
638 #ifdef ACPI_BAT_DEBUG
639 	aprint_debug_dev(dv, "received notify message: 0x%x\n", notify);
640 #endif
641 
642 	switch (notify) {
643 	case ACPI_NOTIFY_BusCheck:
644 		break;
645 
646 	case ACPI_NOTIFY_DeviceCheck:
647 	case ACPI_NOTIFY_BatteryInformationChanged:
648 		mutex_enter(&sc->sc_mtx);
649 		acpibat_clear_presence(sc);
650 		mutex_exit(&sc->sc_mtx);
651 		rv = AcpiOsQueueForExecution(OSD_PRIORITY_LO,
652 					     acpibat_update, dv);
653 		if (ACPI_FAILURE(rv))
654 			aprint_error_dev(dv,
655 			    "unable to queue status check: %s\n",
656 			    AcpiFormatException(rv));
657 		break;
658 
659 	case ACPI_NOTIFY_BatteryStatusChanged:
660 		mutex_enter(&sc->sc_mtx);
661 		acpibat_clear_stat(sc);
662 		mutex_exit(&sc->sc_mtx);
663 		rv = AcpiOsQueueForExecution(OSD_PRIORITY_LO,
664 					     acpibat_update, dv);
665 		if (ACPI_FAILURE(rv))
666 			aprint_error_dev(dv,
667 			    "unable to queue status check: %s\n",
668 			    AcpiFormatException(rv));
669 		break;
670 
671 	default:
672 		aprint_error_dev(dv,
673 		    "received unknown notify message: 0x%x\n", notify);
674 	}
675 }
676 
677 static void
678 acpibat_init_envsys(device_t dv)
679 {
680 	struct acpibat_softc *sc = device_private(dv);
681 	int capunit, rateunit;
682 
683 	if (sc->sc_flags & ABAT_F_PWRUNIT_MA) {
684 		capunit = ENVSYS_SAMPHOUR;
685 		rateunit = ENVSYS_SAMPS;
686 	} else {
687 		capunit = ENVSYS_SWATTHOUR;
688 		rateunit = ENVSYS_SWATTS;
689 	}
690 
691 #define INITDATA(index, unit, string) \
692 	sc->sc_data[index].sensor = index;				\
693 	sc->sc_data[index].units = unit;     				\
694 	sc->sc_data[index].state = ENVSYS_SVALID;			\
695  	strlcpy(sc->sc_data[index].desc, string,			\
696  	    sizeof(sc->sc_data[index].desc));
697 
698 	INITDATA(ACPIBAT_PRESENT, ENVSYS_INDICATOR, "present");
699 	INITDATA(ACPIBAT_DCAPACITY, capunit, "design cap");
700 	INITDATA(ACPIBAT_LFCCAPACITY, capunit, "last full cap");
701 	INITDATA(ACPIBAT_TECHNOLOGY, ENVSYS_INTEGER, "technology");
702 	INITDATA(ACPIBAT_DVOLTAGE, ENVSYS_SVOLTS_DC, "design voltage");
703 	INITDATA(ACPIBAT_WCAPACITY, capunit, "warn cap");
704 	INITDATA(ACPIBAT_LCAPACITY, capunit, "low cap");
705 	INITDATA(ACPIBAT_VOLTAGE, ENVSYS_SVOLTS_DC, "voltage");
706 	INITDATA(ACPIBAT_CHARGERATE, rateunit, "charge rate");
707 	INITDATA(ACPIBAT_DISCHARGERATE, rateunit, "discharge rate");
708 	INITDATA(ACPIBAT_CAPACITY, capunit, "charge");
709 	INITDATA(ACPIBAT_CHARGING, ENVSYS_BATTERY_CHARGE, "charging");
710 	INITDATA(ACPIBAT_CHARGE_STATE, ENVSYS_BATTERY_CAPACITY, "charge state");
711 
712 #undef INITDATA
713 
714 	/* Enable monitoring for the charge state sensor */
715 	sc->sc_data[ACPIBAT_CHARGE_STATE].monitor = true;
716 	sc->sc_data[ACPIBAT_CHARGE_STATE].flags |= ENVSYS_FMONSTCHANGED;
717 
718 	/* Disable userland monitoring on these sensors */
719 	sc->sc_data[ACPIBAT_VOLTAGE].flags = ENVSYS_FMONNOTSUPP;
720 	sc->sc_data[ACPIBAT_CHARGERATE].flags = ENVSYS_FMONNOTSUPP;
721 	sc->sc_data[ACPIBAT_DISCHARGERATE].flags = ENVSYS_FMONNOTSUPP;
722 	sc->sc_data[ACPIBAT_DCAPACITY].flags = ENVSYS_FMONNOTSUPP;
723 	sc->sc_data[ACPIBAT_LFCCAPACITY].flags = ENVSYS_FMONNOTSUPP;
724 	sc->sc_data[ACPIBAT_TECHNOLOGY].flags = ENVSYS_FMONNOTSUPP;
725 	sc->sc_data[ACPIBAT_DVOLTAGE].flags = ENVSYS_FMONNOTSUPP;
726 	sc->sc_data[ACPIBAT_WCAPACITY].flags = ENVSYS_FMONNOTSUPP;
727 	sc->sc_data[ACPIBAT_LCAPACITY].flags = ENVSYS_FMONNOTSUPP;
728 
729 	sc->sc_sysmon.sme_sensor_data = sc->sc_data;
730 	sc->sc_sysmon.sme_name = device_xname(dv);
731 	sc->sc_sysmon.sme_cookie = dv;
732 	sc->sc_sysmon.sme_gtredata = acpibat_gtredata;
733 	sc->sc_sysmon.sme_nsensors = ACPIBAT_NSENSORS;
734 	sc->sc_sysmon.sme_class = SME_CLASS_BATTERY;
735 
736 	sc->sc_updateinterval.tv_sec = 1;
737 	sc->sc_updateinterval.tv_usec = 0;
738 
739 	if (sysmon_envsys_register(&sc->sc_sysmon))
740 		aprint_error_dev(dv, "unable to register with sysmon\n");
741 }
742 
743 static int
744 acpibat_gtredata(struct sysmon_envsys *sme, envsys_data_t *edata)
745 {
746 	device_t dv = sme->sme_cookie;
747 	struct acpibat_softc *sc = device_private(dv);
748 
749 	if (ratecheck(&sc->sc_lastupdate, &sc->sc_updateinterval))
750 		acpibat_update(dv);
751 
752 	return 0;
753 }
754