1 /* $NetBSD: acpi_apm.c,v 1.13 2008/03/07 21:45:08 cube Exp $ */
4 * Copyright (c) 2006 The NetBSD Foundation, Inc.
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
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>
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
= {
71 acpiapm_get_capabilities
,
75 #define DPRINTF(a) uprintf a
80 #ifndef ACPI_APM_DEFAULT_STANDBY_STATE
81 #define ACPI_APM_DEFAULT_STANDBY_STATE (1)
83 #ifndef ACPI_APM_DEFAULT_SUSPEND_STATE
84 #define ACPI_APM_DEFAULT_SUSPEND_STATE (3)
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
;
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
);
110 acpiapm_match(device_t parent
, cfdata_t match
, void *aux
)
117 acpiapm_attach(device_t parent
, device_t self
, void *aux
)
119 struct apm_softc
*sc
= device_private(self
);
122 sc
->sc_ops
= &acpiapm_accessops
;
123 sc
->sc_cookie
= parent
;
124 sc
->sc_vers
= 0x0102;
126 sc
->sc_hwflags
= APM_F_DONT_RUN_HOOKS
;
131 get_state_value(int id
)
133 const int states
[] = {
141 if (id
< ACPI_APM_STATE_MIN
|| id
> ACPI_APM_STATE_MAX
)
142 return ACPI_STATE_S0
;
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
;
157 ref
= &suspend_state
;
158 cap
= APM_GLOBAL_SUSPEND
;
163 node
.sysctl_data
= &newstate
;
164 error
= sysctl_lookup(SYSCTLFN_CALL(&node
));
165 if (error
|| newp
== NULL
)
168 if (newstate
< ACPI_APM_STATE_MIN
|| newstate
> ACPI_APM_STATE_MAX
)
172 oldcap
= capabilities
;
173 capabilities
= newstate
!= 0 ? oldcap
| cap
: oldcap
& ~cap
;
174 if ((capabilities
^ oldcap
) != 0)
175 capability_changed
= 1;
180 SYSCTL_SETUP(sysctl_acpiapm_setup
, "sysctl machdep.acpiapm subtree setup")
182 const struct sysctlnode
*node
;
184 if (sysctl_createv(clog
, 0, NULL
, NULL
,
186 CTLTYPE_NODE
, "machdep", NULL
,
187 NULL
, 0, NULL
, 0, CTL_MACHDEP
, CTL_EOL
))
190 if (sysctl_createv(clog
, 0, NULL
, &node
,
192 CTLTYPE_NODE
, "acpiapm", NULL
,
194 CTL_MACHDEP
, CTL_CREATE
, CTL_EOL
))
196 acpiapm_node
= node
->sysctl_num
;
198 if (sysctl_createv(clog
, 0, NULL
, &node
,
200 CTLTYPE_INT
, "standby", NULL
,
201 &sysctl_state
, 0, NULL
, 0,
202 CTL_MACHDEP
, acpiapm_node
, CTL_CREATE
, CTL_EOL
))
204 standby_node
= node
->sysctl_num
;
206 if (sysctl_createv(clog
, 0, NULL
, NULL
,
208 CTLTYPE_INT
, "suspend", NULL
,
209 &sysctl_state
, 0, NULL
, 0,
210 CTL_MACHDEP
, acpiapm_node
, CTL_CREATE
, CTL_EOL
))
214 /*****************************************************************************
215 * Minimalistic ACPI /dev/apm emulation support, for ACPI suspend
216 *****************************************************************************/
220 acpiapm_disconnect(void *opaque
)
227 acpiapm_enable(void *opaque
, int onoff
)
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
;
243 case APM_SYS_STANDBY
:
244 acpi_enter_sleep_state(sc
, get_state_value(standby_state
));
247 case APM_SYS_SUSPEND
:
248 acpi_enter_sleep_state(sc
, get_state_value(suspend_state
));
253 case APM_LASTREQ_INPROG
:
255 case APM_LASTREQ_REJECTED
:
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 | \
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
);
300 if (sysmonioctl_envsys(0, ENVSYS_GTREINFO
, (void *)&ebis
, 0,
301 NULL
) || (ebis
.validflags
& ENVSYS_FVALID
) == 0)
304 if (sysmonioctl_envsys(0, ENVSYS_GTREDATA
, (void *)&etds
, 0,
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)
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;
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
) {
339 else if (strstr(desc
, " discharge rate")) {
340 discharge
+= data
/ 1000;
344 sysmonclose_envsys(0, 0, 0, &lwp0
);
347 if (warncap
!= -1 && cap
< warncap
)
348 pinfo
->battery_flags
|= APM_BATT_FLAG_CRITICAL
;
349 else if (lowcap
!= -1) {
351 pinfo
->battery_flags
|= APM_BATT_FLAG_LOW
;
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) {
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
;
371 pinfo
->battery_flags
|= APM_BATT_FLAG_LOW
;
374 DPRINTF(("%d %d %d %d %d %d\n", cap
, warncap
, lowcap
, lastcap
, descap
,
376 DPRINTF(("pinfo %d %d %d\n", pinfo
->battery_flags
,
377 pinfo
->battery_life
, pinfo
->battery_life
));
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
;
393 *event_type
= APM_NORMAL_RESUME
;
398 return APM_ERR_NOEVENTS
;
403 acpiapm_cpu_busy(void *opaque
)
410 acpiapm_cpu_idle(void *opaque
)
417 acpiapm_get_capabilities(void *opaque
, u_int
*numbatts
,
421 *capflags
= capabilities
;