Subject: port-i386/34117: enhancement for acpiapm(4)
To: None <port-i386-maintainer@netbsd.org, gnats-admin@netbsd.org,>
From: None <tshiozak@netbsd.org>
List: netbsd-bugs
Date: 07/30/2006 21:20:00
>Number: 34117
>Category: port-i386
>Synopsis: enhancement for acpiapm(4)
>Confidential: no
>Severity: non-critical
>Priority: low
>Responsible: port-i386-maintainer
>State: open
>Class: change-request
>Submitter-Id: net
>Arrival-Date: Sun Jul 30 21:20:00 +0000 2006
>Originator: Takuya SHIOZAKI
>Release: trunk
>Organization:
>Environment:
NetBSD aoi.bogus-hosts.imou.to 3.99.22 NetBSD 3.99.22 (AOI) #13: Fri Jul 21 03:54:24 JST 2006 tshiozak@aoi.bogus-hosts.imou.to:/home/current-build/src/sys/arch/i386/compile/AOI i386
>Description:
add/fix to acpiapm(4):
- make sleep state corresponding to standby/suspend changeable by using sysctl,
- make sure to generate APM events appropriately.
- and arrange the code for getting power status.
>How-To-Repeat:
>Fix:
Index: dev/acpi/acpi_apm.c
===================================================================
RCS file: /home/tshiozak/project/loox/cvs2/src/sys/dev/acpi/acpi_apm.c,v
retrieving revision 1.1.1.1
diff -u -r1.1.1.1 acpi_apm.c
--- dev/acpi/acpi_apm.c 19 Jul 2006 17:00:22 -0000 1.1.1.1
+++ dev/acpi/acpi_apm.c 24 Jul 2006 20:04:39 -0000
@@ -55,7 +55,7 @@
#include <sys/envsys.h>
#include <dev/sysmon/sysmonvar.h>
-#include <dev/acpi/acpica.h>
+#include <dev/acpi/acpica.h>
#include <dev/apm/apmvar.h>
static void acpiapm_disconnect(void *);
@@ -84,21 +84,43 @@
#define DPRINTF(a)
#endif
+#ifndef ACPI_APM_DEFAULT_STANDBY_STATE
+#define ACPI_APM_DEFAULT_STANDBY_STATE (1)
+#endif
+#ifndef ACPI_APM_DEFAULT_SUSPEND_STATE
+#define ACPI_APM_DEFAULT_SUSPEND_STATE (3)
+#endif
+#define ACPI_APM_DEFAULT_CAP \
+ ((ACPI_APM_DEFAULT_STANDBY_STATE!=0 ? APM_GLOBAL_STANDBY : 0) | \
+ (ACPI_APM_DEFAULT_SUSPEND_STATE!=0 ? APM_GLOBAL_SUSPEND : 0))
+#define ACPI_APM_STATE_MIN (0)
+#define ACPI_APM_STATE_MAX (4)
+
+/* It is assumed that there is only acpiapm instance. */
+static int resumed = 0, capability_changed = 0;
+static int standby_state = ACPI_APM_DEFAULT_STANDBY_STATE;
+static int suspend_state = ACPI_APM_DEFAULT_SUSPEND_STATE;
+static int capabilities = ACPI_APM_DEFAULT_CAP;
+static int acpiapm_node = CTL_EOL, standby_node = CTL_EOL;
+
struct acpi_softc;
extern ACPI_STATUS acpi_enter_sleep_state(struct acpi_softc *, int);
static int acpiapm_match(struct device *, struct cfdata *, void *);
static void acpiapm_attach(struct device *, struct device *, void *);
+static int sysctl_state(SYSCTLFN_PROTO);
CFATTACH_DECL(acpiapm, sizeof(struct apm_softc),
acpiapm_match, acpiapm_attach, NULL, NULL);
static int
+/*ARGSUSED*/
acpiapm_match(struct device *parent, struct cfdata *match, void *aux)
{
return apm_match();
}
static void
+/*ARGSUSED*/
acpiapm_attach(struct device *parent, struct device *self, void *aux)
{
struct apm_softc *sc = (struct apm_softc *)self;
@@ -111,17 +133,103 @@
apm_attach(sc);
}
+static int
+get_state_value(int id)
+{
+ const int states[] = {
+ ACPI_STATE_S0,
+ ACPI_STATE_S1,
+ ACPI_STATE_S2,
+ ACPI_STATE_S3,
+ ACPI_STATE_S4
+ };
+
+ if (id < ACPI_APM_STATE_MIN || id > ACPI_APM_STATE_MAX)
+ return ACPI_STATE_S0;
+
+ return states[id];
+}
+
+static int
+sysctl_state(SYSCTLFN_ARGS)
+{
+ int newstate, error, *ref, cap, oldcap;
+ struct sysctlnode node;
+
+ if (rnode->sysctl_num == standby_node) {
+ ref = &standby_state;
+ cap = APM_GLOBAL_STANDBY;
+ } else {
+ ref = &suspend_state;
+ cap = APM_GLOBAL_SUSPEND;
+ }
+
+ newstate = *ref;
+ node = *rnode;
+ node.sysctl_data = &newstate;
+ error = sysctl_lookup(SYSCTLFN_CALL(&node));
+ if (error || newp == NULL)
+ return error;
+
+ if (newstate < ACPI_APM_STATE_MIN || newstate > ACPI_APM_STATE_MAX)
+ return EINVAL;
+
+ *ref = newstate;
+ oldcap = capabilities;
+ capabilities = newstate != 0 ? oldcap | cap : oldcap & ~cap;
+ if ((capabilities ^ oldcap) != 0)
+ capability_changed = 1;
+
+ return 0;
+}
+
+SYSCTL_SETUP(sysctl_acpiapm_setup, "sysctl machdep.acpiapm subtree setup")
+{
+ const struct sysctlnode *node;
+
+ if (sysctl_createv(clog, 0, NULL, NULL,
+ CTLFLAG_PERMANENT,
+ CTLTYPE_NODE, "machdep", NULL,
+ NULL, 0, NULL, 0, CTL_MACHDEP, CTL_EOL))
+ return;
+
+ if (sysctl_createv(clog, 0, NULL, &node,
+ CTLFLAG_PERMANENT,
+ CTLTYPE_NODE, "acpiapm", NULL,
+ NULL, 0, NULL, 0,
+ CTL_MACHDEP, CTL_CREATE, CTL_EOL))
+ return;
+ acpiapm_node = node->sysctl_num;
+
+ if (sysctl_createv(clog, 0, NULL, &node,
+ CTLFLAG_READWRITE,
+ CTLTYPE_INT, "standby", NULL,
+ &sysctl_state, 0, NULL, 0,
+ CTL_MACHDEP, acpiapm_node, CTL_CREATE, CTL_EOL))
+ return;
+ standby_node = node->sysctl_num;
+
+ if (sysctl_createv(clog, 0, NULL, NULL,
+ CTLFLAG_READWRITE,
+ CTLTYPE_INT, "suspend", NULL,
+ &sysctl_state, 0, NULL, 0,
+ CTL_MACHDEP, acpiapm_node, CTL_CREATE, CTL_EOL))
+ return;
+}
+
/*****************************************************************************
* Minimalistic ACPI /dev/apm emulation support, for ACPI suspend
*****************************************************************************/
static void
+/*ARGSUSED*/
acpiapm_disconnect(void *opaque)
{
return;
}
static void
+/*ARGSUSED*/
acpiapm_enable(void *opaque, int onoff)
{
return;
@@ -139,10 +247,12 @@
case APM_SYS_READY:
break;
case APM_SYS_STANDBY:
- acpi_enter_sleep_state(sc, ACPI_STATE_S1);
+ acpi_enter_sleep_state(sc, get_state_value(standby_state));
+ resumed = 1;
break;
case APM_SYS_SUSPEND:
- acpi_enter_sleep_state(sc, ACPI_STATE_S3);
+ acpi_enter_sleep_state(sc, get_state_value(suspend_state));
+ resumed = 1;
break;
case APM_SYS_OFF:
break;
@@ -156,8 +266,12 @@
}
static int
+/*ARGSUSED*/
acpiapm_get_powstat(void *opaque, u_int batteryid, struct apm_power_info *pinfo)
{
+#define APM_BATT_FLAG_WATERMARK_MASK (APM_BATT_FLAG_CRITICAL | \
+ APM_BATT_FLAG_LOW | \
+ APM_BATT_FLAG_HIGH)
int i, curcap, lowcap, warncap, cap, descap, lastcap, discharge;
envsys_tre_data_t etds;
envsys_basic_info_t ebis;
@@ -167,11 +281,11 @@
(void)memset(pinfo, 0, sizeof(*pinfo));
pinfo->ac_state = APM_AC_UNKNOWN;
pinfo->minutes_valid = 0;
- pinfo->minutes_left = 0xffff;
+ pinfo->minutes_left = 0xffff; /* unknown */
pinfo->batteryid = 0;
pinfo->nbattery = 1;
pinfo->battery_flags = 0;
- pinfo->battery_state = APM_BATT_UNKNOWN;
+ pinfo->battery_state = APM_BATT_UNKNOWN; /* ignored */
pinfo->battery_life = APM_BATT_LIFE_UNKNOWN;
for (i = 0;; i++) {
@@ -196,14 +310,11 @@
continue;
if (strstr(desc, " disconnected")) {
pinfo->ac_state = data ? APM_AC_OFF : APM_AC_ON;
- break;
- } else if (strstr(desc, " present") && data == 0) {
+ } else if (strstr(desc, " present") && data == 0)
pinfo->battery_flags |= APM_BATT_FLAG_NO_SYSTEM_BATTERY;
- pinfo->battery_state = APM_BATT_ABSENT;
- } else if (strstr(desc, " charging") && data) {
+ else if (strstr(desc, " charging") && data)
pinfo->battery_flags |= APM_BATT_FLAG_CHARGING;
- pinfo->battery_state = APM_BATT_CHARGING;
- } else if (strstr(desc, " discharging") && data)
+ else if (strstr(desc, " discharging") && data)
pinfo->battery_flags &= ~APM_BATT_FLAG_CHARGING;
else if (strstr(desc, " warn cap"))
warncap = data / 1000;
@@ -221,12 +332,13 @@
}
if (cap != -1) {
- if (warncap != -1 && cap < warncap) {
+ if (warncap != -1 && cap < warncap)
pinfo->battery_flags |= APM_BATT_FLAG_CRITICAL;
- pinfo->battery_state = APM_BATT_CRITICAL;
- } else if (lowcap != -1 && cap < lowcap) {
- pinfo->battery_flags |= APM_BATT_FLAG_LOW;
- pinfo->battery_state = APM_BATT_LOW;
+ else if (lowcap != -1) {
+ if (cap < lowcap)
+ pinfo->battery_flags |= APM_BATT_FLAG_LOW;
+ else
+ pinfo->battery_flags |= APM_BATT_FLAG_HIGH;
}
if (lastcap != -1 && lastcap != 0)
pinfo->battery_life = 100 * cap / lastcap;
@@ -235,44 +347,64 @@
}
if ((pinfo->battery_flags & APM_BATT_FLAG_CHARGING) == 0) {
+ /* discharging */
if (discharge != -1 && cap != -1 && discharge != 0)
pinfo->minutes_left = 60 * cap / discharge;
- if (pinfo->battery_state == APM_BATT_UNKNOWN)
- pinfo->battery_state = pinfo->ac_state == APM_AC_ON ?
- APM_BATT_HIGH : APM_BATT_LOW;
- } else {
- if (pinfo->battery_state == APM_BATT_UNKNOWN)
- pinfo->battery_state = APM_BATT_HIGH;
}
+ if ((pinfo->battery_flags & APM_BATT_FLAG_WATERMARK_MASK) == 0 &&
+ (pinfo->battery_flags & APM_BATT_FLAG_NO_SYSTEM_BATTERY) == 0) {
+ if (pinfo->ac_state == APM_AC_ON)
+ pinfo->battery_flags |= APM_BATT_FLAG_HIGH;
+ else
+ pinfo->battery_flags |= APM_BATT_FLAG_LOW;
+ }
+
DPRINTF(("%d %d %d %d %d %d\n", cap, warncap, lowcap, lastcap, descap,
discharge));
- DPRINTF(("pinfo %d %d %d %d\n", pinfo->battery_flags,
- pinfo->battery_state, pinfo->battery_life, pinfo->battery_life));
+ DPRINTF(("pinfo %d %d %d\n", pinfo->battery_flags,
+ pinfo->battery_life, pinfo->battery_life));
return 0;
}
static int
+/*ARGSUSED*/
acpiapm_get_event(void *opaque, u_int *event_type, u_int *event_info)
{
+ if (capability_changed) {
+ capability_changed = 0;
+ *event_type = APM_CAP_CHANGE;
+ *event_info = 0;
+ return 0;
+ }
+ if (resumed) {
+ resumed = 0;
+ *event_type = APM_NORMAL_RESUME;
+ *event_info = 0;
+ return 0;
+ }
+
return APM_ERR_NOEVENTS;
}
static void
+/*ARGSUSED*/
acpiapm_cpu_busy(void *opaque)
{
return;
}
static void
+/*ARGSUSED*/
acpiapm_cpu_idle(void *opaque)
{
return;
}
static void
+/*ARGSUSED*/
acpiapm_get_capabilities(void *opaque, u_int *numbatts, u_int *capflags)
{
*numbatts = 1;
- *capflags = APM_GLOBAL_STANDBY | APM_GLOBAL_SUSPEND;
+ *capflags = capabilities;
return;
}