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