acpibat.c revision 1.17
1/* $OpenBSD: acpibat.c,v 1.17 2006/02/21 20:25:26 marco Exp $ */
2/*
3 * Copyright (c) 2005 Marco Peereboom <marco@openbsd.org>
4 *
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 */
17
18#include <sys/param.h>
19#include <sys/systm.h>
20#include <sys/device.h>
21#include <sys/malloc.h>
22
23#include <machine/bus.h>
24
25#include <dev/acpi/acpireg.h>
26#include <dev/acpi/acpivar.h>
27#include <dev/acpi/acpidev.h>
28#include <dev/acpi/amltypes.h>
29#include <dev/acpi/dsdt.h>
30
31#include <sys/sensors.h>
32
33int	acpibat_match(struct device *, void *, void *);
34void	acpibat_attach(struct device *, struct device *, void *);
35
36struct acpibat_softc {
37	struct device		sc_dev;
38
39	bus_space_tag_t		sc_iot;
40	bus_space_handle_t	sc_ioh;
41
42	struct acpi_softc	*sc_acpi;
43	struct aml_node		*sc_devnode;
44
45	struct acpibat_bif	sc_bif;
46	struct acpibat_bst	sc_bst;
47
48	struct sensor		sc_sens[8]; /* XXX debug only */
49};
50
51struct cfattach acpibat_ca = {
52	sizeof(struct acpibat_softc), acpibat_match, acpibat_attach
53};
54
55struct cfdriver acpibat_cd = {
56	NULL, "acpibat", DV_DULL
57};
58
59void	acpibat_refresh(void *);
60int	acpibat_getbif(struct acpibat_softc *);
61int	acpibat_getbst(struct acpibat_softc *);
62
63int
64acpibat_match(struct device *parent, void *match, void *aux)
65{
66	struct acpi_attach_args	*aa = aux;
67	struct cfdata		*cf = match;
68
69	/* sanity */
70	if (aa->aaa_name == NULL ||
71	    strcmp(aa->aaa_name, cf->cf_driver->cd_name) != 0 ||
72	    aa->aaa_table != NULL)
73		return (0);
74
75	return (1);
76}
77
78void
79acpibat_attach(struct device *parent, struct device *self, void *aux)
80{
81	struct acpibat_softc	*sc = (struct acpibat_softc *)self;
82	struct acpi_attach_args	*aa = aux;
83	int			i;
84
85	sc->sc_acpi = (struct acpi_softc *)parent;
86	sc->sc_devnode = aa->aaa_node->child;
87
88	if (acpibat_getbif(sc))
89		return;
90
91	acpibat_getbst(sc);
92
93	printf(": model: %s serial: %s type: %s oem: %s\n",
94	    sc->sc_bif.bif_model,
95	    sc->sc_bif.bif_serial,
96	    sc->sc_bif.bif_type,
97	    sc->sc_bif.bif_oem);
98
99	memset(sc->sc_sens, 0, sizeof(sc->sc_sens));
100
101	/* XXX this is for debug only, remove later */
102	for (i = 0; i < 13; i++)
103		strlcpy(sc->sc_sens[i].device, DEVNAME(sc), sizeof(sc->sc_sens[i].device));
104
105	strlcpy(sc->sc_sens[0].desc, "last full capacity", sizeof(sc->sc_sens[2].desc));
106	sc->sc_sens[0].type = SENSOR_PERCENT;
107	sensor_add(&sc->sc_sens[0]);
108	sc->sc_sens[0].value = sc->sc_bif.bif_last_capacity / sc->sc_bif.bif_cap_granu1 * 1000;
109
110	strlcpy(sc->sc_sens[1].desc, "warning capacity", sizeof(sc->sc_sens[1].desc));
111	sc->sc_sens[1].type = SENSOR_PERCENT;
112	sensor_add(&sc->sc_sens[1]);
113	sc->sc_sens[1].value = sc->sc_bif.bif_warning / sc->sc_bif.bif_cap_granu1 * 1000;
114
115	strlcpy(sc->sc_sens[2].desc, "low capacity", sizeof(sc->sc_sens[2].desc));
116	sc->sc_sens[2].type = SENSOR_PERCENT;
117	sensor_add(&sc->sc_sens[2]);
118	sc->sc_sens[2].value = sc->sc_bif.bif_warning / sc->sc_bif.bif_cap_granu1 * 1000;
119
120	strlcpy(sc->sc_sens[3].desc, "voltage", sizeof(sc->sc_sens[3].desc));
121	sc->sc_sens[3].type = SENSOR_VOLTS_DC;
122	sensor_add(&sc->sc_sens[3]);
123	sc->sc_sens[3].status = SENSOR_S_OK;
124	sc->sc_sens[3].value = sc->sc_bif.bif_voltage * 1000;
125
126	strlcpy(sc->sc_sens[4].desc, "state", sizeof(sc->sc_sens[4].desc));
127	sc->sc_sens[4].type = SENSOR_INTEGER;
128	sensor_add(&sc->sc_sens[4]);
129	sc->sc_sens[4].status = SENSOR_S_OK;
130	sc->sc_sens[4].value = sc->sc_bst.bst_state;
131
132	strlcpy(sc->sc_sens[5].desc, "rate", sizeof(sc->sc_sens[5].desc));
133	sc->sc_sens[5].type = SENSOR_INTEGER;
134	sensor_add(&sc->sc_sens[5]);
135	sc->sc_sens[5].value = sc->sc_bst.bst_rate;
136
137	strlcpy(sc->sc_sens[6].desc, "remaining capacity", sizeof(sc->sc_sens[6].desc));
138	sc->sc_sens[6].type = SENSOR_PERCENT;
139	sensor_add(&sc->sc_sens[6]);
140	sc->sc_sens[6].value = sc->sc_bst.bst_capacity / sc->sc_bif.bif_cap_granu1 * 1000;
141
142	strlcpy(sc->sc_sens[7].desc, "current voltage", sizeof(sc->sc_sens[7].desc));
143	sc->sc_sens[7].type = SENSOR_VOLTS_DC;
144	sensor_add(&sc->sc_sens[7]);
145	sc->sc_sens[7].status = SENSOR_S_OK;
146	sc->sc_sens[7].value = sc->sc_bst.bst_voltage * 1000;
147
148	if (sensor_task_register(sc, acpibat_refresh, 10))
149		printf(", unable to register update task\n");
150}
151
152/* XXX this is for debug only, remove later */
153void
154acpibat_refresh(void *arg)
155{
156	struct acpibat_softc	*sc = arg;
157
158	acpibat_getbif(sc);
159	acpibat_getbst(sc);
160
161	sc->sc_sens[0].value = sc->sc_bif.bif_last_capacity / sc->sc_bif.bif_cap_granu1 * 1000;
162	sc->sc_sens[1].value = sc->sc_bif.bif_warning / sc->sc_bif.bif_cap_granu1 * 1000;
163	sc->sc_sens[2].value = sc->sc_bif.bif_warning / sc->sc_bif.bif_cap_granu1 * 1000;
164	sc->sc_sens[3].value = sc->sc_bif.bif_voltage * 1000;
165
166	sc->sc_sens[4].status = SENSOR_S_OK;
167	if (sc->sc_bst.bst_state & BST_DISCHARGE)
168		strlcpy(sc->sc_sens[4].desc, "battery discharging", sizeof(sc->sc_sens[4].desc));
169	else if (sc->sc_bst.bst_state & BST_CHARGE)
170		strlcpy(sc->sc_sens[4].desc, "battery charging", sizeof(sc->sc_sens[4].desc));
171	else if (sc->sc_bst.bst_state & BST_CRITICAL) {
172		strlcpy(sc->sc_sens[4].desc, "battery critical", sizeof(sc->sc_sens[4].desc));
173		sc->sc_sens[4].status = SENSOR_S_CRIT;
174	}
175	sc->sc_sens[4].value = sc->sc_bst.bst_state;
176	sc->sc_sens[5].value = sc->sc_bst.bst_rate;
177	sc->sc_sens[6].value = sc->sc_bst.bst_capacity / sc->sc_bif.bif_cap_granu1 * 1000;
178}
179
180int
181acpibat_getbif(struct acpibat_softc *sc)
182{
183	struct aml_value	res, env;
184	struct acpi_context	*ctx;
185
186	memset(&res, 0, sizeof(res));
187	memset(&env, 0, sizeof(env));
188
189	ctx = NULL;
190	if (aml_eval_name(sc->sc_acpi, sc->sc_devnode, "_STA", &res, &env)) {
191		dnprintf(10, "%s: no _STA\n",
192		    DEVNAME(sc));
193		return (1);
194	}
195
196	if (!(res.v_integer & STA_BATTERY)) {
197		printf(": battery not present\n");
198		return (1);
199	}
200
201	if (aml_eval_name(sc->sc_acpi, sc->sc_devnode, "_BIF", &res, &env)) {
202		dnprintf(10, "%s: no _BIF\n",
203		    DEVNAME(sc));
204		return (1);
205	}
206
207	if (res.length != 13) {
208		printf("%s: invalid _BIF, battery information not saved\n",
209		    DEVNAME(sc));
210		return (1);
211	}
212
213	sc->sc_bif.bif_power_unit = aml_val2int(ctx, res.v_package[0]);
214	sc->sc_bif.bif_capacity = aml_val2int(ctx, res.v_package[1]);
215	sc->sc_bif.bif_last_capacity = aml_val2int(ctx, res.v_package[2]);
216	sc->sc_bif.bif_technology = aml_val2int(ctx, res.v_package[3]);
217	sc->sc_bif.bif_voltage = aml_val2int(ctx, res.v_package[4]);
218	sc->sc_bif.bif_warning = aml_val2int(ctx, res.v_package[5]);
219	sc->sc_bif.bif_low = aml_val2int(ctx, res.v_package[6]);
220	sc->sc_bif.bif_cap_granu1 = aml_val2int(ctx, res.v_package[7]);
221	sc->sc_bif.bif_cap_granu2 = aml_val2int(ctx, res.v_package[8]);
222	sc->sc_bif.bif_model = aml_strval(res.v_package[9]);
223	sc->sc_bif.bif_serial = aml_strval(res.v_package[10]);
224	sc->sc_bif.bif_type = aml_strval(res.v_package[11]);
225	sc->sc_bif.bif_oem = aml_strval(res.v_package[12]);
226
227	dnprintf(60, "power_unit: %u capacity: %u last_cap: %u tech: %u "
228	    "volt: %u warn: %u low: %u gran1: %u gran2: %d model: %s "
229	    "serial: %s type: %s oem: %s\n",
230	    sc->sc_bif.bif_power_unit,
231	    sc->sc_bif.bif_capacity,
232	    sc->sc_bif.bif_last_capacity,
233	    sc->sc_bif.bif_technology,
234	    sc->sc_bif.bif_voltage,
235	    sc->sc_bif.bif_warning,
236	    sc->sc_bif.bif_low,
237	    sc->sc_bif.bif_cap_granu1,
238	    sc->sc_bif.bif_cap_granu2,
239	    sc->sc_bif.bif_model,
240	    sc->sc_bif.bif_serial,
241	    sc->sc_bif.bif_type,
242	    sc->sc_bif.bif_oem);
243
244	return (0);
245}
246
247int
248acpibat_getbst(struct acpibat_softc *sc)
249{
250	struct aml_value	res, env;
251	struct acpi_context	*ctx;
252
253	memset(&res, 0, sizeof(res));
254	memset(&env, 0, sizeof(env));
255
256	ctx = NULL;
257	if (aml_eval_name(sc->sc_acpi, sc->sc_devnode, "_BST", &res, &env)) {
258		dnprintf(10, "%s: no _BST\n",
259		    DEVNAME(sc));
260		return (1);
261	}
262
263	if (res.length != 4) {
264		printf("%s: invalid _BST, battery status not saved\n",
265		    DEVNAME(sc));
266		return (1);
267	}
268
269	sc->sc_bst.bst_state = aml_val2int(ctx, res.v_package[0]);
270	sc->sc_bst.bst_rate = aml_val2int(ctx, res.v_package[1]);
271	sc->sc_bst.bst_capacity = aml_val2int(ctx, res.v_package[2]);
272	sc->sc_bst.bst_voltage = aml_val2int(ctx, res.v_package[3]);
273
274	dnprintf(60, "state: %u rate: %u cap: %u volt: %u ",
275	    sc->sc_bst.bst_state,
276	    sc->sc_bst.bst_rate,
277	    sc->sc_bst.bst_capacity,
278	    sc->sc_bst.bst_voltage);
279
280	return (0);
281}
282