Sync usage with man page.
[netbsd-mini2440.git] / sys / dev / acpi / acpi_apm.c
blob0f24756831f3e0f8e5e06c8a1e1614aededce988
1 /* $NetBSD: acpi_apm.c,v 1.13 2008/03/07 21:45:08 cube Exp $ */
3 /*-
4 * Copyright (c) 2006 The NetBSD Foundation, Inc.
5 * All rights reserved.
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Christos Zoulas and by Jared McNeill.
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.
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
33 * Autoconfiguration support for the Intel ACPI Component Architecture
34 * ACPI reference implementation.
37 #include <sys/cdefs.h>
38 __KERNEL_RCSID(0, "$NetBSD: acpi_apm.c,v 1.13 2008/03/07 21:45:08 cube Exp $");
40 #include <sys/param.h>
41 #include <sys/systm.h>
42 #include <sys/device.h>
43 #include <sys/malloc.h>
44 #include <sys/kernel.h>
45 #include <sys/proc.h>
46 #include <sys/sysctl.h>
47 #include <sys/select.h>
48 #include <sys/envsys.h>
49 #include <dev/sysmon/sysmonvar.h>
51 #include <dev/acpi/acpica.h>
52 #include <dev/apm/apmvar.h>
54 static void acpiapm_disconnect(void *);
55 static void acpiapm_enable(void *, int);
56 static int acpiapm_set_powstate(void *, u_int, u_int);
57 static int acpiapm_get_powstat(void *, u_int, struct apm_power_info *);
58 static int acpiapm_get_event(void *, u_int *, u_int *);
59 static void acpiapm_cpu_busy(void *);
60 static void acpiapm_cpu_idle(void *);
61 static void acpiapm_get_capabilities(void *, u_int *, u_int *);
63 struct apm_accessops acpiapm_accessops = {
64 acpiapm_disconnect,
65 acpiapm_enable,
66 acpiapm_set_powstate,
67 acpiapm_get_powstat,
68 acpiapm_get_event,
69 acpiapm_cpu_busy,
70 acpiapm_cpu_idle,
71 acpiapm_get_capabilities,
74 #ifdef ACPI_APM_DEBUG
75 #define DPRINTF(a) uprintf a
76 #else
77 #define DPRINTF(a)
78 #endif
80 #ifndef ACPI_APM_DEFAULT_STANDBY_STATE
81 #define ACPI_APM_DEFAULT_STANDBY_STATE (1)
82 #endif
83 #ifndef ACPI_APM_DEFAULT_SUSPEND_STATE
84 #define ACPI_APM_DEFAULT_SUSPEND_STATE (3)
85 #endif
86 #define ACPI_APM_DEFAULT_CAP \
87 ((ACPI_APM_DEFAULT_STANDBY_STATE!=0 ? APM_GLOBAL_STANDBY : 0) | \
88 (ACPI_APM_DEFAULT_SUSPEND_STATE!=0 ? APM_GLOBAL_SUSPEND : 0))
89 #define ACPI_APM_STATE_MIN (0)
90 #define ACPI_APM_STATE_MAX (4)
92 /* It is assumed that there is only acpiapm instance. */
93 static int resumed = 0, capability_changed = 0;
94 static int standby_state = ACPI_APM_DEFAULT_STANDBY_STATE;
95 static int suspend_state = ACPI_APM_DEFAULT_SUSPEND_STATE;
96 static int capabilities = ACPI_APM_DEFAULT_CAP;
97 static int acpiapm_node = CTL_EOL, standby_node = CTL_EOL;
99 struct acpi_softc;
100 extern ACPI_STATUS acpi_enter_sleep_state(struct acpi_softc *, int);
101 static int acpiapm_match(device_t, cfdata_t , void *);
102 static void acpiapm_attach(device_t, device_t, void *);
103 static int sysctl_state(SYSCTLFN_PROTO);
105 CFATTACH_DECL_NEW(acpiapm, sizeof(struct apm_softc),
106 acpiapm_match, acpiapm_attach, NULL, NULL);
108 static int
109 /*ARGSUSED*/
110 acpiapm_match(device_t parent, cfdata_t match, void *aux)
112 return apm_match();
115 static void
116 /*ARGSUSED*/
117 acpiapm_attach(device_t parent, device_t self, void *aux)
119 struct apm_softc *sc = device_private(self);
121 sc->sc_dev = self;
122 sc->sc_ops = &acpiapm_accessops;
123 sc->sc_cookie = parent;
124 sc->sc_vers = 0x0102;
125 sc->sc_detail = 0;
126 sc->sc_hwflags = APM_F_DONT_RUN_HOOKS;
127 apm_attach(sc);
130 static int
131 get_state_value(int id)
133 const int states[] = {
134 ACPI_STATE_S0,
135 ACPI_STATE_S1,
136 ACPI_STATE_S2,
137 ACPI_STATE_S3,
138 ACPI_STATE_S4
141 if (id < ACPI_APM_STATE_MIN || id > ACPI_APM_STATE_MAX)
142 return ACPI_STATE_S0;
144 return states[id];
147 static int
148 sysctl_state(SYSCTLFN_ARGS)
150 int newstate, error, *ref, cap, oldcap;
151 struct sysctlnode node;
153 if (rnode->sysctl_num == standby_node) {
154 ref = &standby_state;
155 cap = APM_GLOBAL_STANDBY;
156 } else {
157 ref = &suspend_state;
158 cap = APM_GLOBAL_SUSPEND;
161 newstate = *ref;
162 node = *rnode;
163 node.sysctl_data = &newstate;
164 error = sysctl_lookup(SYSCTLFN_CALL(&node));
165 if (error || newp == NULL)
166 return error;
168 if (newstate < ACPI_APM_STATE_MIN || newstate > ACPI_APM_STATE_MAX)
169 return EINVAL;
171 *ref = newstate;
172 oldcap = capabilities;
173 capabilities = newstate != 0 ? oldcap | cap : oldcap & ~cap;
174 if ((capabilities ^ oldcap) != 0)
175 capability_changed = 1;
177 return 0;
180 SYSCTL_SETUP(sysctl_acpiapm_setup, "sysctl machdep.acpiapm subtree setup")
182 const struct sysctlnode *node;
184 if (sysctl_createv(clog, 0, NULL, NULL,
185 CTLFLAG_PERMANENT,
186 CTLTYPE_NODE, "machdep", NULL,
187 NULL, 0, NULL, 0, CTL_MACHDEP, CTL_EOL))
188 return;
190 if (sysctl_createv(clog, 0, NULL, &node,
191 CTLFLAG_PERMANENT,
192 CTLTYPE_NODE, "acpiapm", NULL,
193 NULL, 0, NULL, 0,
194 CTL_MACHDEP, CTL_CREATE, CTL_EOL))
195 return;
196 acpiapm_node = node->sysctl_num;
198 if (sysctl_createv(clog, 0, NULL, &node,
199 CTLFLAG_READWRITE,
200 CTLTYPE_INT, "standby", NULL,
201 &sysctl_state, 0, NULL, 0,
202 CTL_MACHDEP, acpiapm_node, CTL_CREATE, CTL_EOL))
203 return;
204 standby_node = node->sysctl_num;
206 if (sysctl_createv(clog, 0, NULL, NULL,
207 CTLFLAG_READWRITE,
208 CTLTYPE_INT, "suspend", NULL,
209 &sysctl_state, 0, NULL, 0,
210 CTL_MACHDEP, acpiapm_node, CTL_CREATE, CTL_EOL))
211 return;
214 /*****************************************************************************
215 * Minimalistic ACPI /dev/apm emulation support, for ACPI suspend
216 *****************************************************************************/
218 static void
219 /*ARGSUSED*/
220 acpiapm_disconnect(void *opaque)
222 return;
225 static void
226 /*ARGSUSED*/
227 acpiapm_enable(void *opaque, int onoff)
229 return;
232 static int
233 acpiapm_set_powstate(void *opaque, u_int devid, u_int powstat)
235 struct acpi_softc *sc = device_private((device_t)opaque);
237 if (devid != APM_DEV_ALLDEVS)
238 return APM_ERR_UNRECOG_DEV;
240 switch (powstat) {
241 case APM_SYS_READY:
242 break;
243 case APM_SYS_STANDBY:
244 acpi_enter_sleep_state(sc, get_state_value(standby_state));
245 resumed = 1;
246 break;
247 case APM_SYS_SUSPEND:
248 acpi_enter_sleep_state(sc, get_state_value(suspend_state));
249 resumed = 1;
250 break;
251 case APM_SYS_OFF:
252 break;
253 case APM_LASTREQ_INPROG:
254 break;
255 case APM_LASTREQ_REJECTED:
256 break;
259 return 0;
262 static int
263 /*ARGSUSED*/
264 acpiapm_get_powstat(void *opaque, u_int batteryid,
265 struct apm_power_info *pinfo)
267 #define APM_BATT_FLAG_WATERMARK_MASK (APM_BATT_FLAG_CRITICAL | \
268 APM_BATT_FLAG_LOW | \
269 APM_BATT_FLAG_HIGH)
270 int i, curcap, lowcap, warncap, cap, descap, lastcap, discharge;
271 int cap_valid, lastcap_valid, discharge_valid;
272 envsys_tre_data_t etds;
273 envsys_basic_info_t ebis;
275 /* Denote most variables as unitialized. */
276 curcap = lowcap = warncap = descap = -1;
278 /* Prepare to aggregate these two variables over all batteries. */
279 cap = lastcap = discharge = 0;
280 cap_valid = lastcap_valid = discharge_valid = 0;
282 (void)memset(pinfo, 0, sizeof(*pinfo));
283 pinfo->ac_state = APM_AC_UNKNOWN;
284 pinfo->minutes_valid = 0;
285 pinfo->minutes_left = 0;
286 pinfo->batteryid = 0;
287 pinfo->nbattery = 0; /* to be incremented as batteries are found */
288 pinfo->battery_flags = 0;
289 pinfo->battery_state = APM_BATT_UNKNOWN; /* ignored */
290 pinfo->battery_life = APM_BATT_LIFE_UNKNOWN;
292 sysmonopen_envsys(0, 0, 0, &lwp0);
294 for (i = 0;; i++) {
295 const char *desc;
296 int data;
297 int flags;
299 ebis.sensor = i;
300 if (sysmonioctl_envsys(0, ENVSYS_GTREINFO, (void *)&ebis, 0,
301 NULL) || (ebis.validflags & ENVSYS_FVALID) == 0)
302 break;
303 etds.sensor = i;
304 if (sysmonioctl_envsys(0, ENVSYS_GTREDATA, (void *)&etds, 0,
305 NULL))
306 continue;
307 desc = ebis.desc;
308 flags = etds.validflags;
309 data = etds.cur.data_s;
311 DPRINTF(("%d %s %d %d\n", i, desc, data, flags));
312 if ((flags & ENVSYS_FCURVALID) == 0)
313 continue;
314 if (strstr(desc, " connected")) {
315 pinfo->ac_state = data ? APM_AC_ON : APM_AC_OFF;
316 } else if (strstr(desc, " present") && data == 0)
317 pinfo->battery_flags |= APM_BATT_FLAG_NO_SYSTEM_BATTERY;
318 else if (strstr(desc, " charging") && data)
319 pinfo->battery_flags |= APM_BATT_FLAG_CHARGING;
320 else if (strstr(desc, " charging") && !data)
321 pinfo->battery_flags &= ~APM_BATT_FLAG_CHARGING;
322 else if (strstr(desc, " warn cap"))
323 warncap = data / 1000;
324 else if (strstr(desc, " low cap"))
325 lowcap = data / 1000;
326 else if (strstr(desc, " last full cap")) {
327 lastcap += data / 1000;
328 lastcap_valid = 1;
330 else if (strstr(desc, " design cap"))
331 descap = data / 1000;
332 else if (strstr(desc, " charge") &&
333 strstr(desc, " charge rate") == NULL &&
334 strstr(desc, " charge state") == NULL) {
335 cap += data / 1000;
336 cap_valid = 1;
337 pinfo->nbattery++;
339 else if (strstr(desc, " discharge rate")) {
340 discharge += data / 1000;
341 discharge_valid = 1;
344 sysmonclose_envsys(0, 0, 0, &lwp0);
346 if (cap_valid > 0) {
347 if (warncap != -1 && cap < warncap)
348 pinfo->battery_flags |= APM_BATT_FLAG_CRITICAL;
349 else if (lowcap != -1) {
350 if (cap < lowcap)
351 pinfo->battery_flags |= APM_BATT_FLAG_LOW;
352 else
353 pinfo->battery_flags |= APM_BATT_FLAG_HIGH;
355 if (lastcap_valid > 0 && lastcap != 0)
356 pinfo->battery_life = 100 * cap / lastcap;
357 else if (descap != -1 && descap != 0)
358 pinfo->battery_life = 100 * cap / descap;
361 if ((pinfo->battery_flags & APM_BATT_FLAG_CHARGING) == 0) {
362 /* discharging */
363 if (discharge != -1 && cap != -1 && discharge != 0)
364 pinfo->minutes_left = 60 * cap / discharge;
366 if ((pinfo->battery_flags & APM_BATT_FLAG_WATERMARK_MASK) == 0 &&
367 (pinfo->battery_flags & APM_BATT_FLAG_NO_SYSTEM_BATTERY) == 0) {
368 if (pinfo->ac_state == APM_AC_ON)
369 pinfo->battery_flags |= APM_BATT_FLAG_HIGH;
370 else
371 pinfo->battery_flags |= APM_BATT_FLAG_LOW;
374 DPRINTF(("%d %d %d %d %d %d\n", cap, warncap, lowcap, lastcap, descap,
375 discharge));
376 DPRINTF(("pinfo %d %d %d\n", pinfo->battery_flags,
377 pinfo->battery_life, pinfo->battery_life));
378 return 0;
381 static int
382 /*ARGSUSED*/
383 acpiapm_get_event(void *opaque, u_int *event_type, u_int *event_info)
385 if (capability_changed) {
386 capability_changed = 0;
387 *event_type = APM_CAP_CHANGE;
388 *event_info = 0;
389 return 0;
391 if (resumed) {
392 resumed = 0;
393 *event_type = APM_NORMAL_RESUME;
394 *event_info = 0;
395 return 0;
398 return APM_ERR_NOEVENTS;
401 static void
402 /*ARGSUSED*/
403 acpiapm_cpu_busy(void *opaque)
405 return;
408 static void
409 /*ARGSUSED*/
410 acpiapm_cpu_idle(void *opaque)
412 return;
415 static void
416 /*ARGSUSED*/
417 acpiapm_get_capabilities(void *opaque, u_int *numbatts,
418 u_int *capflags)
420 *numbatts = 1;
421 *capflags = capabilities;
422 return;