1 /* $NetBSD: acpi_bat.c,v 1.36.2.1 2004/07/02 17:27:10 he 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.36.2.1 2004/07/02 17:27:10 he 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 <dev/sysmon/sysmonvar.h>
96
97 #include <dev/acpi/acpica.h>
98 #include <dev/acpi/acpireg.h>
99 #include <dev/acpi/acpivar.h>
100
101 /* sensor indexes */
102 #define ACPIBAT_PRESENT 0
103 #define ACPIBAT_DCAPACITY 1
104 #define ACPIBAT_LFCCAPACITY 2
105 #define ACPIBAT_TECHNOLOGY 3
106 #define ACPIBAT_DVOLTAGE 4
107 #define ACPIBAT_WCAPACITY 5
108 #define ACPIBAT_LCAPACITY 6
109 #define ACPIBAT_VOLTAGE 7
110 #define ACPIBAT_CHARGERATE 8
111 #define ACPIBAT_DISCHARGERATE 9
112 #define ACPIBAT_CAPACITY 10
113 #define ACPIBAT_CHARGING 11
114 #define ACPIBAT_DISCHARGING 12
115 #define ACPIBAT_NSENSORS 13 /* number of sensors */
116
117 const struct envsys_range acpibat_range_amp[] = {
118 { 0, 1, ENVSYS_SVOLTS_DC },
119 { 1, 2, ENVSYS_SAMPS },
120 { 2, 3, ENVSYS_SAMPHOUR },
121 { 1, 0, -1 },
122 };
123
124 const struct envsys_range acpibat_range_watt[] = {
125 { 0, 1, ENVSYS_SVOLTS_DC },
126 { 1, 2, ENVSYS_SWATTS },
127 { 2, 3, ENVSYS_SWATTHOUR },
128 { 1, 0, -1 },
129 };
130
131 struct acpibat_softc {
132 struct device sc_dev; /* base device glue */
133 struct acpi_devnode *sc_node; /* our ACPI devnode */
134 int sc_flags; /* see below */
135 int sc_available; /* available information level */
136
137 struct sysmon_envsys sc_sysmon;
138 struct envsys_basic_info sc_info[ACPIBAT_NSENSORS];
139 struct envsys_tre_data sc_data[ACPIBAT_NSENSORS];
140
141 struct simplelock sc_lock;
142
143 struct timeval sc_lastupdate, sc_updateinterval;
144 };
145
146 static const char * const bat_hid[] = {
147 "PNP0C0A",
148 NULL
149 };
150
151 /*
152 * These flags are used to examine the battery device data returned from
153 * the ACPI interface, specifically the "battery status"
154 */
155 #define ACPIBAT_PWRUNIT_MA 0x00000001 /* mA not mW */
156
157 /*
158 * These flags are used to examine the battery charge/discharge/critical
159 * state returned from a get-status command.
160 */
161 #define ACPIBAT_ST_DISCHARGING 0x00000001 /* battery is discharging */
162 #define ACPIBAT_ST_CHARGING 0x00000002 /* battery is charging */
163 #define ACPIBAT_ST_CRITICAL 0x00000004 /* battery is critical */
164
165 /*
166 * Flags for battery status from _STA return
167 */
168 #define ACPIBAT_STA_PRESENT 0x00000010 /* battery present */
169
170 /*
171 * These flags are used to set internal state in our softc.
172 */
173 #define ABAT_F_VERBOSE 0x01 /* verbose events */
174 #define ABAT_F_PWRUNIT_MA 0x02 /* mA instead of mW */
175 #define ABAT_F_PRESENT 0x04 /* is the battery present? */
176 #define ABAT_F_LOCKED 0x08 /* is locked? */
177
178 #define ABAT_SET(sc, f) (void)((sc)->sc_flags |= (f))
179 #define ABAT_CLEAR(sc, f) (void)((sc)->sc_flags &= ~(f))
180 #define ABAT_ISSET(sc, f) ((sc)->sc_flags & (f))
181
182 /*
183 * Available info level
184 */
185
186 #define ABAT_ALV_NONE 0 /* none is available */
187 #define ABAT_ALV_PRESENCE 1 /* presence info is available */
188 #define ABAT_ALV_INFO 2 /* battery info is available */
189 #define ABAT_ALV_STAT 3 /* battery status is available */
190
191 #define ABAT_ASSERT_LOCKED(sc) \
192 do { \
193 if (!((sc)->sc_flags & ABAT_F_LOCKED)) \
194 panic("acpi_bat (expected to be locked)"); \
195 } while(/*CONSTCOND*/0)
196 #define ABAT_ASSERT_UNLOCKED(sc) \
197 do { \
198 if (((sc)->sc_flags & ABAT_F_LOCKED)) \
199 panic("acpi_bat (expected to be unlocked)"); \
200 } while(/*CONSTCOND*/0)
201 #define ABAT_LOCK(sc, s) \
202 do { \
203 ABAT_ASSERT_UNLOCKED(sc); \
204 (s) = splhigh(); \
205 simple_lock(&(sc)->sc_lock); \
206 ABAT_SET((sc), ABAT_F_LOCKED); \
207 } while(/*CONSTCOND*/0)
208 #define ABAT_UNLOCK(sc, s) \
209 do { \
210 ABAT_ASSERT_LOCKED(sc); \
211 ABAT_CLEAR((sc), ABAT_F_LOCKED); \
212 simple_unlock(&(sc)->sc_lock); \
213 splx((s)); \
214 } while(/*CONSTCOND*/0)
215
216 int acpibat_match(struct device *, struct cfdata *, void *);
217 void acpibat_attach(struct device *, struct device *, void *);
218
219 CFATTACH_DECL(acpibat, sizeof(struct acpibat_softc),
220 acpibat_match, acpibat_attach, NULL, NULL);
221
222 static void acpibat_clear_presence(struct acpibat_softc *);
223 static void acpibat_clear_info(struct acpibat_softc *);
224 static void acpibat_clear_stat(struct acpibat_softc *);
225 static int acpibat_battery_present(struct acpibat_softc *);
226 static ACPI_STATUS acpibat_get_status(struct acpibat_softc *);
227 static ACPI_STATUS acpibat_get_info(struct acpibat_softc *);
228 static void acpibat_print_info(struct acpibat_softc *);
229 static void acpibat_print_stat(struct acpibat_softc *);
230 static void acpibat_update(void *);
231
232 static void acpibat_init_envsys(struct acpibat_softc *);
233 static void acpibat_notify_handler(ACPI_HANDLE, UINT32, void *context);
234 static int acpibat_gtredata(struct sysmon_envsys *, struct envsys_tre_data *);
235 static int acpibat_streinfo(struct sysmon_envsys *, struct envsys_basic_info *);
236
237 /*
238 * acpibat_match:
239 *
240 * Autoconfiguration `match' routine.
241 */
242 int
243 acpibat_match(struct device *parent, struct cfdata *match, void *aux)
244 {
245 struct acpi_attach_args *aa = aux;
246
247 if (aa->aa_node->ad_type != ACPI_TYPE_DEVICE)
248 return (0);
249
250 return (acpi_match_hid(aa->aa_node->ad_devinfo, bat_hid));
251 }
252
253 /*
254 * acpibat_attach:
255 *
256 * Autoconfiguration `attach' routine.
257 */
258 void
259 acpibat_attach(struct device *parent, struct device *self, void *aux)
260 {
261 struct acpibat_softc *sc = (void *) self;
262 struct acpi_attach_args *aa = aux;
263 ACPI_STATUS rv;
264
265 printf(": ACPI Battery (Control Method)\n");
266
267 sc->sc_node = aa->aa_node;
268 simple_lock_init(&sc->sc_lock);
269
270 rv = AcpiInstallNotifyHandler(sc->sc_node->ad_handle,
271 ACPI_DEVICE_NOTIFY,
272 acpibat_notify_handler, sc);
273 if (ACPI_FAILURE(rv)) {
274 printf("%s: unable to register DEVICE NOTIFY handler: %s\n",
275 sc->sc_dev.dv_xname, AcpiFormatException(rv));
276 return;
277 }
278
279 /* XXX See acpibat_notify_handler() */
280 rv = AcpiInstallNotifyHandler(sc->sc_node->ad_handle,
281 ACPI_SYSTEM_NOTIFY,
282 acpibat_notify_handler, sc);
283 if (ACPI_FAILURE(rv)) {
284 printf("%s: unable to register SYSTEM NOTIFY handler: %s\n",
285 sc->sc_dev.dv_xname, AcpiFormatException(rv));
286 return;
287 }
288
289 #ifdef ACPI_BAT_DEBUG
290 ABAT_SET(sc, ABAT_F_VERBOSE);
291 #endif
292
293 acpibat_init_envsys(sc);
294 }
295
296 /*
297 * clear informations
298 */
299
300 void
301 acpibat_clear_presence(struct acpibat_softc *sc)
302 {
303
304 ABAT_ASSERT_LOCKED(sc);
305
306 acpibat_clear_info(sc);
307 sc->sc_available = ABAT_ALV_NONE;
308 ABAT_CLEAR(sc, ABAT_F_PRESENT);
309 }
310
311 void
312 acpibat_clear_info(struct acpibat_softc *sc)
313 {
314
315 ABAT_ASSERT_LOCKED(sc);
316
317 acpibat_clear_stat(sc);
318 if (sc->sc_available>ABAT_ALV_PRESENCE)
319 sc->sc_available = ABAT_ALV_PRESENCE;
320 sc->sc_data[ACPIBAT_DCAPACITY].validflags &= ~ENVSYS_FCURVALID;
321 sc->sc_data[ACPIBAT_LFCCAPACITY].validflags &= ~ENVSYS_FCURVALID;
322 sc->sc_data[ACPIBAT_CAPACITY].validflags &= ~ENVSYS_FMAXVALID;
323 sc->sc_data[ACPIBAT_TECHNOLOGY].validflags &= ~ENVSYS_FCURVALID;
324 sc->sc_data[ACPIBAT_DVOLTAGE].validflags &= ~ENVSYS_FCURVALID;
325 sc->sc_data[ACPIBAT_WCAPACITY].validflags &= ~(ENVSYS_FCURVALID | ENVSYS_FMAXVALID | ENVSYS_FFRACVALID);
326 sc->sc_data[ACPIBAT_LCAPACITY].validflags &= ~(ENVSYS_FCURVALID | ENVSYS_FMAXVALID | ENVSYS_FFRACVALID);
327 }
328
329 void
330 acpibat_clear_stat(struct acpibat_softc *sc)
331 {
332
333 ABAT_ASSERT_LOCKED(sc);
334
335 if (sc->sc_available>ABAT_ALV_INFO)
336 sc->sc_available = ABAT_ALV_INFO;
337 sc->sc_data[ACPIBAT_CHARGERATE].validflags &= ~ENVSYS_FCURVALID;
338 sc->sc_data[ACPIBAT_DISCHARGERATE].validflags &= ~ENVSYS_FCURVALID;
339 sc->sc_data[ACPIBAT_CAPACITY].validflags &= ~(ENVSYS_FCURVALID | ENVSYS_FFRACVALID);
340 sc->sc_data[ACPIBAT_CAPACITY].warnflags = 0;
341 sc->sc_data[ACPIBAT_VOLTAGE].validflags &= ~ENVSYS_FCURVALID;
342 sc->sc_data[ACPIBAT_CHARGING].validflags &= ~ENVSYS_FCURVALID;
343 sc->sc_data[ACPIBAT_DISCHARGING].validflags &= ~ENVSYS_FCURVALID;
344 }
345
346
347 /*
348 * returns 0 for no battery, 1 for present, and -1 on error
349 */
350 int
351 acpibat_battery_present(struct acpibat_softc *sc)
352 {
353 u_int32_t sta;
354 int s;
355 ACPI_INTEGER val;
356 ACPI_STATUS rv;
357
358 rv = acpi_eval_integer(sc->sc_node->ad_handle, "_STA", &val);
359 if (ACPI_FAILURE(rv)) {
360 printf("%s: failed to evaluate _STA: %s\n",
361 sc->sc_dev.dv_xname, AcpiFormatException(rv));
362 return (-1);
363 }
364
365 sta = (u_int32_t)val;
366
367 ABAT_LOCK(sc, s);
368 sc->sc_available = ABAT_ALV_PRESENCE;
369 if (sta & ACPIBAT_STA_PRESENT) {
370 ABAT_SET(sc, ABAT_F_PRESENT);
371 sc->sc_data[ACPIBAT_PRESENT].cur.data_s = 1;
372 } else
373 sc->sc_data[ACPIBAT_PRESENT].cur.data_s = 0;
374 sc->sc_data[ACPIBAT_PRESENT].validflags |= ENVSYS_FCURVALID;
375 ABAT_UNLOCK(sc, s);
376
377 return ((sta & ACPIBAT_STA_PRESENT)?1:0);
378 }
379
380 /*
381 * acpibat_get_info
382 *
383 * Get, and possibly display, the battery info.
384 */
385
386 ACPI_STATUS
387 acpibat_get_info(struct acpibat_softc *sc)
388 {
389 ACPI_OBJECT *p1, *p2;
390 ACPI_STATUS rv;
391 ACPI_BUFFER buf;
392 int capunit, rateunit, s;
393
394 rv = acpi_eval_struct(sc->sc_node->ad_handle, "_BIF", &buf);
395 if (ACPI_FAILURE(rv)) {
396 printf("%s: failed to evaluate _BIF: %s\n",
397 sc->sc_dev.dv_xname, AcpiFormatException(rv));
398 return (rv);
399 }
400 p1 = (ACPI_OBJECT *)buf.Pointer;
401
402 if (p1->Type != ACPI_TYPE_PACKAGE) {
403 printf("%s: expected PACKAGE, got %d\n", sc->sc_dev.dv_xname,
404 p1->Type);
405 goto out;
406 }
407 if (p1->Package.Count < 13) {
408 printf("%s: expected 13 elts, got %d\n",
409 sc->sc_dev.dv_xname, p1->Package.Count);
410 goto out;
411 }
412 p2 = p1->Package.Elements;
413
414 ABAT_LOCK(sc, s);
415 if ((p2[0].Integer.Value & ACPIBAT_PWRUNIT_MA) != 0) {
416 ABAT_SET(sc, ABAT_F_PWRUNIT_MA);
417 sc->sc_sysmon.sme_ranges = acpibat_range_amp;
418 capunit = ENVSYS_SAMPHOUR;
419 rateunit = ENVSYS_SAMPS;
420 } else {
421 ABAT_CLEAR(sc, ABAT_F_PWRUNIT_MA);
422 sc->sc_sysmon.sme_ranges = acpibat_range_watt;
423 capunit = ENVSYS_SWATTHOUR;
424 rateunit = ENVSYS_SWATTS;
425 }
426
427 #define INITDATA(index, unit) \
428 sc->sc_data[index].units = unit; \
429 sc->sc_info[index].units = unit;
430
431 INITDATA(ACPIBAT_DCAPACITY, capunit);
432 INITDATA(ACPIBAT_LFCCAPACITY, capunit);
433 INITDATA(ACPIBAT_WCAPACITY, capunit);
434 INITDATA(ACPIBAT_LCAPACITY, capunit);
435 INITDATA(ACPIBAT_CHARGERATE, rateunit);
436 INITDATA(ACPIBAT_DISCHARGERATE, rateunit);
437 INITDATA(ACPIBAT_CAPACITY, capunit);
438
439 #undef INITDATA
440
441 sc->sc_data[ACPIBAT_DCAPACITY].cur.data_s = p2[1].Integer.Value * 1000;
442 sc->sc_data[ACPIBAT_DCAPACITY].validflags |= ENVSYS_FCURVALID;
443 sc->sc_data[ACPIBAT_LFCCAPACITY].cur.data_s = p2[2].Integer.Value * 1000;
444 sc->sc_data[ACPIBAT_LFCCAPACITY].validflags |= ENVSYS_FCURVALID;
445 sc->sc_data[ACPIBAT_CAPACITY].max.data_s = p2[2].Integer.Value * 1000;
446 sc->sc_data[ACPIBAT_CAPACITY].validflags |= ENVSYS_FMAXVALID;
447 sc->sc_data[ACPIBAT_TECHNOLOGY].cur.data_s = p2[3].Integer.Value;
448 sc->sc_data[ACPIBAT_TECHNOLOGY].validflags |= ENVSYS_FCURVALID;
449 sc->sc_data[ACPIBAT_DVOLTAGE].cur.data_s = p2[4].Integer.Value * 1000;
450 sc->sc_data[ACPIBAT_DVOLTAGE].validflags |= ENVSYS_FCURVALID;
451 sc->sc_data[ACPIBAT_WCAPACITY].cur.data_s = p2[5].Integer.Value * 1000;
452 sc->sc_data[ACPIBAT_WCAPACITY].max.data_s = p2[2].Integer.Value * 1000;
453 sc->sc_data[ACPIBAT_WCAPACITY].validflags |= ENVSYS_FCURVALID | ENVSYS_FMAXVALID | ENVSYS_FFRACVALID;
454 sc->sc_data[ACPIBAT_LCAPACITY].cur.data_s = p2[6].Integer.Value * 1000;
455 sc->sc_data[ACPIBAT_LCAPACITY].max.data_s = p2[2].Integer.Value * 1000;
456 sc->sc_data[ACPIBAT_LCAPACITY].validflags |= ENVSYS_FCURVALID | ENVSYS_FMAXVALID | ENVSYS_FFRACVALID;
457 sc->sc_available = ABAT_ALV_INFO;
458 ABAT_UNLOCK(sc, s);
459
460 printf("%s: battery info: %s, %s, %s, %s\n", sc->sc_dev.dv_xname,
461 p2[12].String.Pointer, p2[11].String.Pointer,
462 p2[9].String.Pointer, p2[10].String.Pointer);
463
464 rv = AE_OK;
465
466 out:
467 AcpiOsFree(buf.Pointer);
468 return (rv);
469 }
470
471 /*
472 * acpibat_get_status:
473 *
474 * Get, and possibly display, the current battery line status.
475 */
476 ACPI_STATUS
477 acpibat_get_status(struct acpibat_softc *sc)
478 {
479 int flags, status, s;
480 ACPI_OBJECT *p1, *p2;
481 ACPI_STATUS rv;
482 ACPI_BUFFER buf;
483
484 rv = acpi_eval_struct(sc->sc_node->ad_handle, "_BST", &buf);
485 if (ACPI_FAILURE(rv)) {
486 printf("%s: failed to evaluate _BST: %s\n",
487 sc->sc_dev.dv_xname, AcpiFormatException(rv));
488 return (rv);
489 }
490 p1 = (ACPI_OBJECT *)buf.Pointer;
491
492 if (p1->Type != ACPI_TYPE_PACKAGE) {
493 printf("bat: expected PACKAGE, got %d\n", p1->Type);
494 rv = AE_ERROR;
495 goto out;
496 }
497 if (p1->Package.Count < 4) {
498 printf("bat: expected 4 elts, got %d\n", p1->Package.Count);
499 rv = AE_ERROR;
500 goto out;
501 }
502 p2 = p1->Package.Elements;
503
504 ABAT_LOCK(sc, s);
505 status = p2[0].Integer.Value;
506 sc->sc_data[ACPIBAT_CHARGERATE].validflags &= ~ENVSYS_FCURVALID;
507 sc->sc_data[ACPIBAT_DISCHARGERATE].validflags &= ~ENVSYS_FCURVALID;
508 if (p2[1].Integer.Value != -1) {
509 if (status & ACPIBAT_ST_CHARGING) {
510 sc->sc_data[ACPIBAT_CHARGERATE].cur.data_s = p2[1].Integer.Value * 1000;
511 sc->sc_data[ACPIBAT_CHARGERATE].validflags |= ENVSYS_FCURVALID;
512 } else if (status & ACPIBAT_ST_DISCHARGING) {
513 sc->sc_data[ACPIBAT_DISCHARGERATE].cur.data_s = p2[1].Integer.Value * 1000;
514 sc->sc_data[ACPIBAT_DISCHARGERATE].validflags |= ENVSYS_FCURVALID;
515 }
516 }
517 sc->sc_data[ACPIBAT_CAPACITY].cur.data_s = p2[2].Integer.Value * 1000;
518 sc->sc_data[ACPIBAT_CAPACITY].validflags |= ENVSYS_FCURVALID | ENVSYS_FFRACVALID;
519 sc->sc_data[ACPIBAT_VOLTAGE].cur.data_s = p2[3].Integer.Value * 1000;
520 sc->sc_data[ACPIBAT_VOLTAGE].validflags |= ENVSYS_FCURVALID;
521 flags = 0;
522 if (sc->sc_data[ACPIBAT_CAPACITY].cur.data_s <
523 sc->sc_data[ACPIBAT_WCAPACITY].cur.data_s)
524 flags |= ENVSYS_WARN_UNDER;
525 if (status & ACPIBAT_ST_CRITICAL)
526 flags |= ENVSYS_WARN_CRITUNDER;
527 sc->sc_data[ACPIBAT_CAPACITY].warnflags = flags;
528 sc->sc_data[ACPIBAT_CHARGING].cur.data_s =
529 ((status & ACPIBAT_ST_CHARGING) != 0);
530 sc->sc_data[ACPIBAT_CHARGING].validflags |= ENVSYS_FCURVALID;
531 sc->sc_data[ACPIBAT_DISCHARGING].cur.data_s =
532 ((status & ACPIBAT_ST_DISCHARGING) != 0);
533 sc->sc_data[ACPIBAT_DISCHARGING].validflags |= ENVSYS_FCURVALID;
534 sc->sc_available = ABAT_ALV_STAT;
535 ABAT_UNLOCK(sc, s);
536
537 rv = AE_OK;
538
539 out:
540 AcpiOsFree(buf.Pointer);
541 return (rv);
542 }
543
544 #define SCALE(x) ((x)/1000000), (((x)%1000000)/1000)
545 #define CAPUNITS(sc) (ABAT_ISSET((sc), ABAT_F_PWRUNIT_MA)?"Ah":"Wh")
546 #define RATEUNITS(sc) (ABAT_ISSET((sc), ABAT_F_PWRUNIT_MA)?"A":"W")
547 static void
548 acpibat_print_info(struct acpibat_softc *sc)
549 {
550 const char *tech;
551
552 if (sc->sc_data[ACPIBAT_TECHNOLOGY].cur.data_s)
553 tech = "secondary";
554 else
555 tech = "primary";
556
557 printf("%s: %s battery, Design %d.%03d%s, Last full %d.%03d%s"
558 "Warn %d.%03d%s Low %d.%03d%s\n",
559 sc->sc_dev.dv_xname, tech,
560 SCALE(sc->sc_data[ACPIBAT_DCAPACITY].cur.data_s), CAPUNITS(sc),
561 SCALE(sc->sc_data[ACPIBAT_LFCCAPACITY].cur.data_s),CAPUNITS(sc),
562 SCALE(sc->sc_data[ACPIBAT_WCAPACITY].cur.data_s), CAPUNITS(sc),
563 SCALE(sc->sc_data[ACPIBAT_LCAPACITY].cur.data_s), CAPUNITS(sc));
564 }
565
566 static void
567 acpibat_print_stat(struct acpibat_softc *sc)
568 {
569 const char *capstat, *chargestat;
570 int percent, denom;
571
572 percent = 0;
573
574 if (sc->sc_data[ACPIBAT_CAPACITY].warnflags&ENVSYS_WARN_CRITUNDER)
575 capstat = "CRITICAL ";
576 else if (sc->sc_data[ACPIBAT_CAPACITY].warnflags&ENVSYS_WARN_UNDER)
577 capstat = "UNDER ";
578 else
579 capstat = "";
580 if (sc->sc_data[ACPIBAT_CHARGING].cur.data_s)
581 chargestat = "charging";
582 else if (sc->sc_data[ACPIBAT_DISCHARGING].cur.data_s)
583 chargestat = "discharging";
584 else
585 chargestat = "idling";
586 denom = sc->sc_data[ACPIBAT_DCAPACITY].cur.data_s / 100;
587 if (denom > 0)
588 percent = (sc->sc_data[ACPIBAT_CAPACITY].cur.data_s) / denom;
589 printf("%s: %s%s: %d.%03dV cap %d.%03d%s (%d%%) rate %d.%03d%s\n",
590 sc->sc_dev.dv_xname,
591 capstat, chargestat,
592 SCALE(sc->sc_data[ACPIBAT_VOLTAGE].cur.data_s),
593 SCALE(sc->sc_data[ACPIBAT_CAPACITY].cur.data_s), CAPUNITS(sc),
594 percent,
595 SCALE(sc->sc_data[ACPIBAT_CHARGING].cur.data_s ?
596 sc->sc_data[ACPIBAT_CHARGERATE].cur.data_s :
597 sc->sc_data[ACPIBAT_DISCHARGING].cur.data_s ?
598 sc->sc_data[ACPIBAT_DISCHARGERATE].cur.data_s : 0),
599 RATEUNITS(sc));
600 }
601
602 static void
603 acpibat_update(void *arg)
604 {
605 struct acpibat_softc *sc = arg;
606
607 if (sc->sc_available < ABAT_ALV_INFO) {
608 /* current information is invalid */
609 #if 0
610 /*
611 * XXX: The driver sometimes unaware that the battery exist.
612 * (i.e. just after the boot or resuming)
613 * Thus, the driver should always check it here.
614 */
615 if (sc->sc_available < ABAT_ALV_PRESENCE)
616 #endif
617 /* presence is invalid */
618 if (acpibat_battery_present(sc)<0) {
619 /* error */
620 printf("%s: cannot get battery presence.\n",
621 sc->sc_dev.dv_xname);
622 return;
623 }
624 if (ABAT_ISSET(sc, ABAT_F_PRESENT)) {
625 /* the battery is present. */
626 if (ABAT_ISSET(sc, ABAT_F_VERBOSE))
627 printf("%s: battery is present.\n",
628 sc->sc_dev.dv_xname);
629 if (ACPI_FAILURE(acpibat_get_info(sc)))
630 return;
631 if (ABAT_ISSET(sc, ABAT_F_VERBOSE))
632 acpibat_print_info(sc);
633 } else {
634 /* the battery is not present. */
635 if (ABAT_ISSET(sc, ABAT_F_VERBOSE))
636 printf("%s: battery is not present.\n",
637 sc->sc_dev.dv_xname);
638 return;
639 }
640 } else {
641 /* current information is valid */
642 if (!ABAT_ISSET(sc, ABAT_F_PRESENT)) {
643 /* the battery is not present. */
644 return;
645 }
646 }
647
648 if (ACPI_FAILURE(acpibat_get_status(sc)))
649 return;
650
651 if (ABAT_ISSET(sc, ABAT_F_VERBOSE))
652 acpibat_print_stat(sc);
653 }
654
655 /*
656 * acpibat_notify_handler:
657 *
658 * Callback from ACPI interrupt handler to notify us of an event.
659 */
660 void
661 acpibat_notify_handler(ACPI_HANDLE handle, UINT32 notify, void *context)
662 {
663 struct acpibat_softc *sc = context;
664 int rv, s;
665
666 #ifdef ACPI_BAT_DEBUG
667 printf("%s: received notify message: 0x%x\n",
668 sc->sc_dev.dv_xname, notify);
669 #endif
670
671 switch (notify) {
672 case ACPI_NOTIFY_BusCheck:
673 break;
674
675 case ACPI_NOTIFY_DeviceCheck:
676 case ACPI_NOTIFY_BatteryInformationChanged:
677 ABAT_LOCK(sc, s);
678 acpibat_clear_presence(sc);
679 ABAT_UNLOCK(sc, s);
680 rv = AcpiOsQueueForExecution(OSD_PRIORITY_LO,
681 acpibat_update, sc);
682 if (ACPI_FAILURE(rv))
683 printf("%s: unable to queue status check: %s\n",
684 sc->sc_dev.dv_xname, AcpiFormatException(rv));
685 break;
686
687 case ACPI_NOTIFY_BatteryStatusChanged:
688 ABAT_LOCK(sc, s);
689 acpibat_clear_stat(sc);
690 ABAT_UNLOCK(sc, s);
691 rv = AcpiOsQueueForExecution(OSD_PRIORITY_LO,
692 acpibat_update, sc);
693 if (ACPI_FAILURE(rv))
694 printf("%s: unable to queue status check: %s\n",
695 sc->sc_dev.dv_xname, AcpiFormatException(rv));
696 break;
697
698 default:
699 printf("%s: received unknown notify message: 0x%x\n",
700 sc->sc_dev.dv_xname, notify);
701 }
702 }
703
704 void
705 acpibat_init_envsys(struct acpibat_softc *sc)
706 {
707 int capunit, rateunit;
708
709 #if 0
710 if (sc->sc_flags & ABAT_F_PWRUNIT_MA) {
711 #endif
712 /* XXX */
713 sc->sc_sysmon.sme_ranges = acpibat_range_amp;
714 capunit = ENVSYS_SAMPHOUR;
715 rateunit = ENVSYS_SAMPS;
716 #if 0
717 } else {
718 sc->sc_sysmon.sme_ranges = acpibat_range_watt;
719 capunit = ENVSYS_SWATTHOUR;
720 rateunit = ENVSYS_SWATTS;
721 }
722 #endif
723
724 #define INITDATA(index, unit, string) \
725 sc->sc_data[index].sensor = index; \
726 sc->sc_data[index].units = unit; \
727 sc->sc_data[index].validflags = ENVSYS_FVALID; \
728 sc->sc_data[index].warnflags = 0; \
729 sc->sc_info[index].sensor = index; \
730 sc->sc_info[index].units = unit; \
731 sc->sc_info[index].validflags = ENVSYS_FVALID; \
732 snprintf(sc->sc_info[index].desc, sizeof(sc->sc_info->desc), \
733 "%s %s", sc->sc_dev.dv_xname, string); \
734
735 INITDATA(ACPIBAT_PRESENT, ENVSYS_INDICATOR, "present");
736 INITDATA(ACPIBAT_DCAPACITY, capunit, "design cap");
737 INITDATA(ACPIBAT_LFCCAPACITY, capunit, "last full cap");
738 INITDATA(ACPIBAT_TECHNOLOGY, ENVSYS_INTEGER, "technology");
739 INITDATA(ACPIBAT_DVOLTAGE, ENVSYS_SVOLTS_DC, "design voltage");
740 INITDATA(ACPIBAT_WCAPACITY, capunit, "warn cap");
741 INITDATA(ACPIBAT_LCAPACITY, capunit, "low cap");
742 INITDATA(ACPIBAT_VOLTAGE, ENVSYS_SVOLTS_DC, "voltage");
743 INITDATA(ACPIBAT_CHARGERATE, rateunit, "charge rate");
744 INITDATA(ACPIBAT_DISCHARGERATE, rateunit, "discharge rate");
745 INITDATA(ACPIBAT_CAPACITY, capunit, "charge");
746 INITDATA(ACPIBAT_CHARGING, ENVSYS_INDICATOR, "charging");
747 INITDATA(ACPIBAT_DISCHARGING, ENVSYS_INDICATOR, "discharging");
748
749 #undef INITDATA
750
751 sc->sc_sysmon.sme_sensor_info = sc->sc_info;
752 sc->sc_sysmon.sme_sensor_data = sc->sc_data;
753 sc->sc_sysmon.sme_cookie = sc;
754 sc->sc_sysmon.sme_gtredata = acpibat_gtredata;
755 sc->sc_sysmon.sme_streinfo = acpibat_streinfo;
756 sc->sc_sysmon.sme_nsensors = ACPIBAT_NSENSORS;
757 sc->sc_sysmon.sme_envsys_version = 1000;
758
759 sc->sc_updateinterval.tv_sec = 1;
760 sc->sc_updateinterval.tv_usec = 0;
761
762 if (sysmon_envsys_register(&sc->sc_sysmon))
763 printf("%s: unable to register with sysmon\n",
764 sc->sc_dev.dv_xname);
765 }
766
767 int
768 acpibat_gtredata(struct sysmon_envsys *sme, struct envsys_tre_data *tred)
769 {
770 struct acpibat_softc *sc = sme->sme_cookie;
771
772 if (ratecheck(&sc->sc_lastupdate, &sc->sc_updateinterval))
773 acpibat_update(sc);
774
775 /* XXX locking */
776 *tred = sc->sc_data[tred->sensor];
777 /* XXX locking */
778
779 return (0);
780 }
781
782 int
783 acpibat_streinfo(struct sysmon_envsys *sme, struct envsys_basic_info *binfo)
784 {
785
786 /* XXX Not implemented */
787 binfo->validflags = 0;
788
789 return (0);
790 }
Cache object: 77fca70b1b534fb478583396fd568007
|