acpi_battery.c revision 143002
1/*-
2 * Copyright (c) 2000 Mitsuru IWASAKI <iwasaki@jp.freebsd.org>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 */
26
27#include <sys/cdefs.h>
28__FBSDID("$FreeBSD: head/sys/dev/acpica/acpi_battery.c 143002 2005-03-02 09:22:34Z obrien $");
29
30#include "opt_acpi.h"
31#include <sys/param.h>
32#include <sys/kernel.h>
33#include <sys/malloc.h>
34#include <sys/bus.h>
35#include <sys/ioccom.h>
36#include <sys/sysctl.h>
37
38#include "acpi.h"
39#include <dev/acpica/acpivar.h>
40#include <dev/acpica/acpiio.h>
41
42MALLOC_DEFINE(M_ACPIBATT, "acpibatt", "ACPI generic battery data");
43
44struct acpi_batteries {
45    TAILQ_ENTRY(acpi_batteries)	link;
46    struct acpi_battdesc	battdesc;
47};
48
49static TAILQ_HEAD(,acpi_batteries) acpi_batteries;
50static int			acpi_batteries_initted;
51static int			acpi_batteries_units;
52static int			acpi_battery_info_expire = 5;
53static struct acpi_battinfo	acpi_battery_battinfo;
54ACPI_SERIAL_DECL(battery, "ACPI generic battery");
55
56int
57acpi_battery_get_info_expire(void)
58{
59    return (acpi_battery_info_expire);
60}
61
62int
63acpi_battery_get_units(void)
64{
65    return (acpi_batteries_units);
66}
67
68int
69acpi_battery_get_battdesc(int logical_unit, struct acpi_battdesc *battdesc)
70{
71    struct acpi_batteries *bp;
72    int error, i;
73
74    error = ENXIO;
75    ACPI_SERIAL_BEGIN(battery);
76    if (logical_unit < 0 || logical_unit >= acpi_batteries_units)
77	goto out;
78
79    i = 0;
80    TAILQ_FOREACH(bp, &acpi_batteries, link) {
81	if (logical_unit == i) {
82	    battdesc->type = bp->battdesc.type;
83	    battdesc->phys_unit = bp->battdesc.phys_unit;
84	    error = 0;
85	    break;
86	}
87	i++;
88    }
89
90out:
91    ACPI_SERIAL_END(battery);
92    return (error);
93}
94
95int
96acpi_battery_get_battinfo(int unit, struct acpi_battinfo *battinfo)
97{
98    struct acpi_battdesc battdesc;
99    int error;
100
101    error = 0;
102    if (unit == -1) {
103	error = acpi_cmbat_get_battinfo(-1, battinfo);
104	goto out;
105    } else {
106	error = acpi_battery_get_battdesc(unit, &battdesc);
107	if (error != 0)
108	    goto out;
109
110	switch (battdesc.type) {
111	case ACPI_BATT_TYPE_CMBAT:
112	    error = acpi_cmbat_get_battinfo(battdesc.phys_unit, battinfo);
113	    break;
114	default:
115	    error = ENXIO;
116	    break;
117	}
118    }
119
120out:
121    return (error);
122}
123
124static int
125acpi_battery_ioctl(u_long cmd, caddr_t addr, void *arg)
126{
127    union acpi_battery_ioctl_arg *ioctl_arg;
128    int	error, logical_unit;
129
130    ioctl_arg = (union acpi_battery_ioctl_arg *)addr;
131    error = 0;
132
133    /*
134     * No security check required: information retrieval only.  If
135     * new functions are added here, a check might be required.
136     */
137    switch (cmd) {
138    case ACPIIO_BATT_GET_UNITS:
139	*(int *)addr = acpi_battery_get_units();
140	break;
141    case ACPIIO_BATT_GET_BATTDESC:
142	logical_unit = ioctl_arg->unit;
143	error = acpi_battery_get_battdesc(logical_unit, &ioctl_arg->battdesc);
144	break;
145    case ACPIIO_BATT_GET_BATTINFO:
146	logical_unit = ioctl_arg->unit;
147	error = acpi_battery_get_battinfo(logical_unit, &ioctl_arg->battinfo);
148	break;
149    default:
150	error = EINVAL;
151	break;
152    }
153
154    return (error);
155}
156
157static int
158acpi_battery_sysctl(SYSCTL_HANDLER_ARGS)
159{
160    int	val, error;
161
162    acpi_battery_get_battinfo(-1, &acpi_battery_battinfo);
163    val = *(u_int *)oidp->oid_arg1;
164    error = sysctl_handle_int(oidp, &val, 0, req);
165    return (error);
166}
167
168static int
169acpi_battery_init(void)
170{
171    struct acpi_softc	*sc;
172    device_t		 dev;
173    int	 		 error;
174
175    ACPI_SERIAL_ASSERT(battery);
176
177    error = ENXIO;
178    dev = devclass_get_device(devclass_find("acpi"), 0);
179    if (dev == NULL)
180	goto out;
181    sc = device_get_softc(dev);
182
183    TAILQ_INIT(&acpi_batteries);
184
185    /* XXX We should back out registered ioctls on error. */
186    error = acpi_register_ioctl(ACPIIO_BATT_GET_UNITS, acpi_battery_ioctl,
187	NULL);
188    if (error != 0)
189	goto out;
190    error = acpi_register_ioctl(ACPIIO_BATT_GET_BATTDESC, acpi_battery_ioctl,
191	NULL);
192    if (error != 0)
193	goto out;
194    error = acpi_register_ioctl(ACPIIO_BATT_GET_BATTINFO, acpi_battery_ioctl,
195	NULL);
196    if (error != 0)
197	goto out;
198
199    sysctl_ctx_init(&sc->acpi_battery_sysctl_ctx);
200    sc->acpi_battery_sysctl_tree = SYSCTL_ADD_NODE(&sc->acpi_battery_sysctl_ctx,
201	SYSCTL_CHILDREN(sc->acpi_sysctl_tree), OID_AUTO, "battery", CTLFLAG_RD,
202	0, "");
203    SYSCTL_ADD_PROC(&sc->acpi_battery_sysctl_ctx,
204	SYSCTL_CHILDREN(sc->acpi_battery_sysctl_tree),
205	OID_AUTO, "life", CTLTYPE_INT | CTLFLAG_RD,
206	&acpi_battery_battinfo.cap, 0, acpi_battery_sysctl, "I", "");
207    SYSCTL_ADD_PROC(&sc->acpi_battery_sysctl_ctx,
208	SYSCTL_CHILDREN(sc->acpi_battery_sysctl_tree),
209	OID_AUTO, "time", CTLTYPE_INT | CTLFLAG_RD,
210	&acpi_battery_battinfo.min, 0, acpi_battery_sysctl, "I", "");
211    SYSCTL_ADD_PROC(&sc->acpi_battery_sysctl_ctx,
212	SYSCTL_CHILDREN(sc->acpi_battery_sysctl_tree),
213	OID_AUTO, "state", CTLTYPE_INT | CTLFLAG_RD,
214	&acpi_battery_battinfo.state, 0, acpi_battery_sysctl, "I", "");
215    SYSCTL_ADD_INT(&sc->acpi_battery_sysctl_ctx,
216	SYSCTL_CHILDREN(sc->acpi_battery_sysctl_tree),
217	OID_AUTO, "units", CTLFLAG_RD, &acpi_batteries_units, 0, "");
218    SYSCTL_ADD_INT(&sc->acpi_battery_sysctl_ctx,
219	SYSCTL_CHILDREN(sc->acpi_battery_sysctl_tree),
220	OID_AUTO, "info_expire", CTLFLAG_RD | CTLFLAG_RW,
221	&acpi_battery_info_expire, 0, "");
222
223    acpi_batteries_initted = TRUE;
224
225out:
226    return (error);
227}
228
229int
230acpi_battery_register(int type, int phys_unit)
231{
232    struct acpi_batteries *bp;
233    int error;
234
235    error = 0;
236    bp = malloc(sizeof(*bp), M_ACPIBATT, M_NOWAIT);
237    if (bp == NULL)
238	return (ENOMEM);
239
240    ACPI_SERIAL_BEGIN(battery);
241    if (!acpi_batteries_initted && (error = acpi_battery_init()) != 0) {
242	printf("acpi_battery_register failed for unit %d\n", phys_unit);
243	goto out;
244    }
245    bp->battdesc.type = type;
246    bp->battdesc.phys_unit = phys_unit;
247    TAILQ_INSERT_TAIL(&acpi_batteries, bp, link);
248    acpi_batteries_units++;
249
250out:
251    ACPI_SERIAL_END(battery);
252    if (error)
253	free(bp, M_ACPIBATT);
254    return (error);
255}
256
257int
258acpi_battery_remove(int type, int phys_unit)
259{
260    struct acpi_batteries *bp, *tmp;
261    int ret;
262
263    ret = ENOENT;
264    ACPI_SERIAL_BEGIN(battery);
265    TAILQ_FOREACH_SAFE(bp, &acpi_batteries, link, tmp) {
266	if (bp->battdesc.type == type && bp->battdesc.phys_unit == phys_unit) {
267	    TAILQ_REMOVE(&acpi_batteries, bp, link);
268	    acpi_batteries_units--;
269	    ret = 0;
270	    break;
271	}
272    }
273    ACPI_SERIAL_END(battery);
274    if (ret == 0)
275	free(bp, M_ACPIBATT);
276    return (ret);
277}
278