Update the suspend/resume user API while maintaining backwards compat.
Improvements: * /etc/rc.suspend,rc.resume are always run, no matter the source of the suspend request (user or kernel, apm or acpi) * suspend now requires positive user acknowledgement. If a user program wants to cancel the suspend, they can. If one of the user programs hangs or doesn't respond within 10 seconds, the system suspends anyway. * /dev/apm is clonable, allowing multiple listeners for suspend events. In the future, xorg-server can use this to be informed about suspend even if there are other listeners (i.e. apmd). Changes: * Two new ACPI ioctls: REQSLPSTATE and ACKSLPSTATE. Request begins the process of suspending by notifying all listeners. acpi is monitored by devd(8) and /dev/apm listener(s) are also counted. Users register their approval or disapproval via Ack. If anyone disapproves, suspend is vetoed. * Old user programs or kernel modules that used SETSLPSTATE continue to work. A message is printed once that this interface is deprecated. * acpiconf gains the -k flag to ack the suspend request. This flag is undocumented on purpose since it's only used by /etc/rc.suspend. It is not intended to be a permanent change and will be removed once a better power API is implemented. * S5 (power off) is no longer supported via acpiconf -s 5 or apm -z/-Z. This restores previous behavior of halt/shutdown -p being the interface. * Miscellaneous improvements to error reporting Approved by: re
This commit is contained in:
parent
f99ccd6438
commit
00a304487f
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=170976
@ -239,6 +239,19 @@ notify 10 {
|
||||
action "logger -p kern.warn 'ZFS: checksum mismatch, zpool=$pool path=$vdev_path offset=$zio_offset size=$zio_size'";
|
||||
};
|
||||
|
||||
# User requested suspend, so perform preparation steps and then execute
|
||||
# the actual suspend process.
|
||||
notify 10 {
|
||||
match "system" "ACPI";
|
||||
match "subsystem" "Suspend";
|
||||
action "/etc/rc.suspend acpi $notify";
|
||||
};
|
||||
notify 10 {
|
||||
match "system" "ACPI";
|
||||
match "subsystem" "Resume";
|
||||
action "/etc/rc.resume acpi $notify";
|
||||
};
|
||||
|
||||
/* EXAMPLES TO END OF FILE
|
||||
|
||||
# The following might be an example of something that a vendor might
|
||||
@ -277,6 +290,7 @@ detach 10 {
|
||||
# Button: Button pressed (0 for power, 1 for sleep)
|
||||
# CMBAT: ACPI battery events
|
||||
# Lid: Lid state (0 is closed, 1 is open)
|
||||
# Suspend, Resume: Suspend and resume notification
|
||||
# Thermal: ACPI thermal zone events
|
||||
#
|
||||
# This example calls a script when the AC state changes, passing the
|
||||
|
@ -30,7 +30,7 @@
|
||||
# sample run command file for APM Resume Event
|
||||
|
||||
if [ $# -ne 2 ]; then
|
||||
echo "Usage: $0 [apm|acpi] [standby,suspend|1-5]"
|
||||
echo "Usage: $0 [apm|acpi] [standby,suspend|1-4]"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
@ -48,9 +48,13 @@ fi
|
||||
# pccardq | awk -F '~' '$5 == "inactive" \
|
||||
# { printf("pccardc power %d 1", $1); }' | sh
|
||||
|
||||
# UHCI has trouble resuming so we just load/unload it. You
|
||||
# should add any other kernel modules you want reloaded here.
|
||||
# kldload usb
|
||||
# If a device driver has problems resuming, try unloading it before
|
||||
# suspend and reloading it on resume. Example:
|
||||
# kldunload usb
|
||||
|
||||
# wpa_supplicant(8) doesn't seem to reassociate during resume. Uncomment
|
||||
# the following to signal it to reassociate.
|
||||
# /usr/sbin/wpa_cli reassociate
|
||||
|
||||
logger -t $subsystem resumed at `date +'%Y%m%d %H:%M:%S'`
|
||||
sync && sync && sync
|
||||
|
@ -30,7 +30,7 @@
|
||||
# sample run command file for APM Suspend Event
|
||||
|
||||
if [ $# -ne 2 ]; then
|
||||
echo "Usage: $0 [apm|acpi] [standby,suspend|1-5]"
|
||||
echo "Usage: $0 [apm|acpi] [standby,suspend|1-4]"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
@ -48,15 +48,20 @@ echo $$ 2> /dev/null > /var/run/rc.suspend.pid
|
||||
# pccardq | awk -F '~' '$5 == "filled" && $4 ~ /sio/ \
|
||||
# { printf("pccardc power %d 0", $1); }' | sh
|
||||
|
||||
# UHCI has trouble resuming so we just load/unload it. You
|
||||
# should add any other kernel modules you want unloaded here.
|
||||
# If a device driver has problems suspending, try unloading it before
|
||||
# suspend and reloading it on resume. Example:
|
||||
# kldunload usb
|
||||
|
||||
logger -t $subsystem suspend at `date +'%Y%m%d %H:%M:%S'`
|
||||
sync && sync && sync
|
||||
[ $subsystem = "apm" ] && sleep 3
|
||||
sleep 3
|
||||
|
||||
rm -f /var/run/rc.suspend.pid
|
||||
[ $subsystem = "apm" ] && zzz
|
||||
if [ $subsystem = "apm" ]; then
|
||||
/usr/sbin/zzz
|
||||
else
|
||||
# Notify the kernel to continue the suspend process
|
||||
/usr/sbin/acpiconf -k 0
|
||||
fi
|
||||
|
||||
exit 0
|
||||
|
@ -136,6 +136,7 @@ static int acpi_probe_order(ACPI_HANDLE handle, int *order);
|
||||
static ACPI_STATUS acpi_probe_child(ACPI_HANDLE handle, UINT32 level,
|
||||
void *context, void **status);
|
||||
static BOOLEAN acpi_MatchHid(ACPI_HANDLE h, const char *hid);
|
||||
static ACPI_STATUS acpi_EnterSleepState(struct acpi_softc *sc, int state);
|
||||
static void acpi_shutdown_final(void *arg, int howto);
|
||||
static void acpi_enable_fixed_events(struct acpi_softc *sc);
|
||||
static int acpi_wake_sleep_prep(ACPI_HANDLE handle, int sstate);
|
||||
@ -410,6 +411,7 @@ acpi_attach(device_t dev)
|
||||
|
||||
sc = device_get_softc(dev);
|
||||
sc->acpi_dev = dev;
|
||||
callout_init(&sc->susp_force_to, TRUE);
|
||||
|
||||
error = ENXIO;
|
||||
|
||||
@ -592,7 +594,7 @@ acpi_attach(device_t dev)
|
||||
|
||||
/* Pick the first valid sleep state for the sleep button default. */
|
||||
sc->acpi_sleep_button_sx = ACPI_S_STATES_MAX + 1;
|
||||
for (state = ACPI_STATE_S1; state < ACPI_STATE_S5; state++)
|
||||
for (state = ACPI_STATE_S1; state <= ACPI_STATE_S4; state++)
|
||||
if (ACPI_SUCCESS(AcpiGetSleepTypeData(state, &TypeA, &TypeB))) {
|
||||
sc->acpi_sleep_button_sx = state;
|
||||
break;
|
||||
@ -2118,6 +2120,150 @@ acpi_SetIntrModel(int model)
|
||||
return (acpi_SetInteger(ACPI_ROOT_OBJECT, "_PIC", model));
|
||||
}
|
||||
|
||||
/*
|
||||
* DEPRECATED. This interface has serious deficiencies and will be
|
||||
* removed.
|
||||
*
|
||||
* Immediately enter the sleep state. In the old model, acpiconf(8) ran
|
||||
* rc.suspend and rc.resume so we don't have to notify devd(8) to do this.
|
||||
*/
|
||||
ACPI_STATUS
|
||||
acpi_SetSleepState(struct acpi_softc *sc, int state)
|
||||
{
|
||||
static int once;
|
||||
|
||||
if (!once) {
|
||||
printf(
|
||||
"warning: acpi_SetSleepState() deprecated, need to update your software\n");
|
||||
once = 1;
|
||||
}
|
||||
return (acpi_EnterSleepState(sc, state));
|
||||
}
|
||||
|
||||
static void
|
||||
acpi_sleep_force(void *arg)
|
||||
{
|
||||
struct acpi_softc *sc;
|
||||
|
||||
printf("acpi: suspend request timed out, forcing sleep now\n");
|
||||
sc = arg;
|
||||
if (ACPI_FAILURE(acpi_EnterSleepState(sc, sc->acpi_next_sstate)))
|
||||
printf("acpi: force sleep state S%d failed\n", sc->acpi_next_sstate);
|
||||
}
|
||||
|
||||
/*
|
||||
* Request that the system enter the given suspend state. All /dev/apm
|
||||
* devices and devd(8) will be notified. Userland then has a chance to
|
||||
* save state and acknowledge the request. The system sleeps once all
|
||||
* acks are in.
|
||||
*/
|
||||
int
|
||||
acpi_ReqSleepState(struct acpi_softc *sc, int state)
|
||||
{
|
||||
struct apm_clone_data *clone;
|
||||
|
||||
if (state < ACPI_STATE_S1 || state > ACPI_STATE_S5)
|
||||
return (EINVAL);
|
||||
|
||||
/* S5 (soft-off) should be entered directly with no waiting. */
|
||||
if (state == ACPI_STATE_S5) {
|
||||
if (ACPI_SUCCESS(acpi_EnterSleepState(sc, state)))
|
||||
return (0);
|
||||
else
|
||||
return (ENXIO);
|
||||
}
|
||||
|
||||
/* If a suspend request is already in progress, just return. */
|
||||
ACPI_LOCK(acpi);
|
||||
if (sc->acpi_next_sstate != 0) {
|
||||
ACPI_UNLOCK(acpi);
|
||||
return (0);
|
||||
}
|
||||
|
||||
/* Record the pending state and notify all apm devices. */
|
||||
sc->acpi_next_sstate = state;
|
||||
STAILQ_FOREACH(clone, &sc->apm_cdevs, entries) {
|
||||
clone->notify_status = APM_EV_NONE;
|
||||
if ((clone->flags & ACPI_EVF_DEVD) == 0) {
|
||||
selwakeuppri(&clone->sel_read, PZERO);
|
||||
KNOTE_UNLOCKED(&clone->sel_read.si_note, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/* Now notify devd(8) also. */
|
||||
acpi_UserNotify("Suspend", ACPI_ROOT_OBJECT, state);
|
||||
|
||||
/*
|
||||
* Set a timeout to fire if userland doesn't ack the suspend request
|
||||
* in time. This way we still eventually go to sleep if we were
|
||||
* overheating or running low on battery, even if userland is hung.
|
||||
* We cancel this timeout once all userland acks are in or the
|
||||
* suspend request is aborted.
|
||||
*/
|
||||
callout_reset(&sc->susp_force_to, 10 * hz, acpi_sleep_force, sc);
|
||||
ACPI_UNLOCK(acpi);
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Acknowledge (or reject) a pending sleep state. The caller has
|
||||
* prepared for suspend and is now ready for it to proceed. If the
|
||||
* error argument is non-zero, it indicates suspend should be cancelled
|
||||
* and gives an errno value describing why. Once all votes are in,
|
||||
* we suspend the system.
|
||||
*/
|
||||
int
|
||||
acpi_AckSleepState(struct apm_clone_data *clone, int error)
|
||||
{
|
||||
struct acpi_softc *sc;
|
||||
int ret, sleeping;
|
||||
|
||||
/* If no pending sleep state, return an error. */
|
||||
ACPI_LOCK(acpi);
|
||||
sc = clone->acpi_sc;
|
||||
if (sc->acpi_next_sstate == 0) {
|
||||
ACPI_UNLOCK(acpi);
|
||||
return (ENXIO);
|
||||
}
|
||||
|
||||
/* Caller wants to abort suspend process. */
|
||||
if (error) {
|
||||
sc->acpi_next_sstate = 0;
|
||||
callout_stop(&sc->susp_force_to);
|
||||
printf("acpi: listener on %s cancelled the pending suspend\n",
|
||||
devtoname(clone->cdev));
|
||||
ACPI_UNLOCK(acpi);
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Mark this device as acking the suspend request. Then, walk through
|
||||
* all devices, seeing if they agree yet. We only count devices that
|
||||
* are writable since read-only devices couldn't ack the request.
|
||||
*/
|
||||
clone->notify_status = APM_EV_ACKED;
|
||||
sleeping = TRUE;
|
||||
STAILQ_FOREACH(clone, &sc->apm_cdevs, entries) {
|
||||
if ((clone->flags & ACPI_EVF_WRITE) != 0 &&
|
||||
clone->notify_status != APM_EV_ACKED) {
|
||||
sleeping = FALSE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* If all devices have voted "yes", we will suspend now. */
|
||||
if (sleeping)
|
||||
callout_stop(&sc->susp_force_to);
|
||||
ACPI_UNLOCK(acpi);
|
||||
ret = 0;
|
||||
if (sleeping) {
|
||||
if (ACPI_FAILURE(acpi_EnterSleepState(sc, sc->acpi_next_sstate)))
|
||||
ret = ENODEV;
|
||||
}
|
||||
|
||||
return (ret);
|
||||
}
|
||||
|
||||
static void
|
||||
acpi_sleep_enable(void *arg)
|
||||
{
|
||||
@ -2134,12 +2280,12 @@ enum acpi_sleep_state {
|
||||
};
|
||||
|
||||
/*
|
||||
* Set the system sleep state
|
||||
* Enter the desired system sleep state.
|
||||
*
|
||||
* Currently we support S1-S5 but S4 is only S4BIOS
|
||||
*/
|
||||
ACPI_STATUS
|
||||
acpi_SetSleepState(struct acpi_softc *sc, int state)
|
||||
static ACPI_STATUS
|
||||
acpi_EnterSleepState(struct acpi_softc *sc, int state)
|
||||
{
|
||||
ACPI_STATUS status;
|
||||
UINT8 TypeA;
|
||||
@ -2148,14 +2294,13 @@ acpi_SetSleepState(struct acpi_softc *sc, int state)
|
||||
|
||||
ACPI_FUNCTION_TRACE_U32((char *)(uintptr_t)__func__, state);
|
||||
|
||||
/* Re-entry once we're suspending is not allowed. */
|
||||
status = AE_OK;
|
||||
ACPI_LOCK(acpi);
|
||||
if (sc->acpi_sleep_disabled) {
|
||||
if (sc->acpi_sstate != ACPI_STATE_S0)
|
||||
status = AE_ERROR;
|
||||
ACPI_UNLOCK(acpi);
|
||||
printf("acpi: suspend request ignored (not ready yet)\n");
|
||||
return (status);
|
||||
return (AE_ERROR);
|
||||
}
|
||||
sc->acpi_sleep_disabled = 1;
|
||||
ACPI_UNLOCK(acpi);
|
||||
@ -2251,6 +2396,7 @@ acpi_SetSleepState(struct acpi_softc *sc, int state)
|
||||
* Back out state according to how far along we got in the suspend
|
||||
* process. This handles both the error and success cases.
|
||||
*/
|
||||
sc->acpi_next_sstate = 0;
|
||||
if (slp_state >= ACPI_SS_GPE_SET) {
|
||||
acpi_wake_prep_walk(state);
|
||||
sc->acpi_sstate = ACPI_STATE_S0;
|
||||
@ -2264,7 +2410,10 @@ acpi_SetSleepState(struct acpi_softc *sc, int state)
|
||||
|
||||
/* Allow another sleep request after a while. */
|
||||
if (state != ACPI_STATE_S5)
|
||||
timeout(acpi_sleep_enable, (caddr_t)sc, hz * ACPI_MINIMUM_AWAKETIME);
|
||||
timeout(acpi_sleep_enable, sc, hz * ACPI_MINIMUM_AWAKETIME);
|
||||
|
||||
/* Run /etc/rc.resume after we are back. */
|
||||
acpi_UserNotify("Resume", ACPI_ROOT_OBJECT, state);
|
||||
|
||||
mtx_unlock(&Giant);
|
||||
return_ACPI_STATUS (status);
|
||||
@ -2574,11 +2723,15 @@ acpi_parse_prw(ACPI_HANDLE h, struct acpi_prw_data *prw)
|
||||
static void
|
||||
acpi_system_eventhandler_sleep(void *arg, int state)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ACPI_FUNCTION_TRACE_U32((char *)(uintptr_t)__func__, state);
|
||||
|
||||
if (state >= ACPI_STATE_S0 && state <= ACPI_S_STATES_MAX)
|
||||
acpi_SetSleepState((struct acpi_softc *)arg, state);
|
||||
/* Request that the system prepare to enter the given suspend state. */
|
||||
ret = acpi_ReqSleepState((struct acpi_softc *)arg, state);
|
||||
if (ret != 0)
|
||||
printf("acpi: request to enter state S%d failed (err %d)\n",
|
||||
state, ret);
|
||||
|
||||
return_VOID;
|
||||
}
|
||||
@ -2840,7 +2993,20 @@ acpiioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flag, d_thread_t *td)
|
||||
|
||||
/* Core system ioctls. */
|
||||
switch (cmd) {
|
||||
case ACPIIO_SETSLPSTATE:
|
||||
case ACPIIO_REQSLPSTATE:
|
||||
state = *(int *)addr;
|
||||
if (state != ACPI_STATE_S5)
|
||||
error = acpi_ReqSleepState(sc, state);
|
||||
else {
|
||||
printf("power off via acpi ioctl not supported\n");
|
||||
error = ENXIO;
|
||||
}
|
||||
break;
|
||||
case ACPIIO_ACKSLPSTATE:
|
||||
error = *(int *)addr;
|
||||
error = acpi_AckSleepState(sc->acpi_clone, error);
|
||||
break;
|
||||
case ACPIIO_SETSLPSTATE: /* DEPRECATED */
|
||||
error = EINVAL;
|
||||
state = *(int *)addr;
|
||||
if (state >= ACPI_STATE_S0 && state <= ACPI_S_STATES_MAX)
|
||||
@ -3171,7 +3337,8 @@ acpi_pm_func(u_long cmd, void *arg, ...)
|
||||
goto out;
|
||||
}
|
||||
|
||||
acpi_SetSleepState(sc, acpi_state);
|
||||
if (ACPI_FAILURE(acpi_EnterSleepState(sc, acpi_state)))
|
||||
error = ENXIO;
|
||||
break;
|
||||
default:
|
||||
error = EINVAL;
|
||||
|
@ -33,7 +33,13 @@
|
||||
/*
|
||||
* Core ACPI subsystem ioctls
|
||||
*/
|
||||
#define ACPIIO_SETSLPSTATE _IOW('P', 3, int)
|
||||
#define ACPIIO_SETSLPSTATE _IOW('P', 3, int) /* DEPRECATED */
|
||||
|
||||
/* Request S1-5 sleep state. User is notified and then sleep proceeds. */
|
||||
#define ACPIIO_REQSLPSTATE _IOW('P', 4, int)
|
||||
|
||||
/* Allow suspend to continue (0) or abort it (errno). */
|
||||
#define ACPIIO_ACKSLPSTATE _IOW('P', 5, int)
|
||||
|
||||
struct acpi_battinfo {
|
||||
int cap; /* percent */
|
||||
|
@ -39,12 +39,14 @@
|
||||
#include <sys/ktr.h>
|
||||
#include <sys/lock.h>
|
||||
#include <sys/mutex.h>
|
||||
#include <sys/selinfo.h>
|
||||
#include <sys/sx.h>
|
||||
#include <sys/sysctl.h>
|
||||
|
||||
#include <machine/bus.h>
|
||||
#include <machine/resource.h>
|
||||
|
||||
struct apm_clone_data;
|
||||
struct acpi_softc {
|
||||
device_t acpi_dev;
|
||||
struct cdev *acpi_dev_t;
|
||||
@ -76,6 +78,11 @@ struct acpi_softc {
|
||||
bus_dmamap_t acpi_wakemap;
|
||||
vm_offset_t acpi_wakeaddr;
|
||||
vm_paddr_t acpi_wakephys;
|
||||
|
||||
int acpi_next_sstate; /* Next suspend Sx state. */
|
||||
struct apm_clone_data *acpi_clone; /* Pseudo-dev for devd(8). */
|
||||
STAILQ_HEAD(,apm_clone_data) apm_cdevs; /* All apm/apmctl/acpi cdevs. */
|
||||
struct callout susp_force_to; /* Force suspend if no acks. */
|
||||
};
|
||||
|
||||
struct acpi_device {
|
||||
@ -89,6 +96,22 @@ struct acpi_device {
|
||||
struct resource_list ad_rl;
|
||||
};
|
||||
|
||||
/* Track device (/dev/{apm,apmctl} and /dev/acpi) notification status. */
|
||||
struct apm_clone_data {
|
||||
STAILQ_ENTRY(apm_clone_data) entries;
|
||||
struct cdev *cdev;
|
||||
int flags;
|
||||
#define ACPI_EVF_NONE 0 /* /dev/apm semantics */
|
||||
#define ACPI_EVF_DEVD 1 /* /dev/acpi is handled via devd(8) */
|
||||
#define ACPI_EVF_WRITE 2 /* Device instance is opened writable. */
|
||||
int notify_status;
|
||||
#define APM_EV_NONE 0 /* Device not yet aware of pending sleep. */
|
||||
#define APM_EV_NOTIFIED 1 /* Device saw next sleep state. */
|
||||
#define APM_EV_ACKED 2 /* Device agreed sleep can occur. */
|
||||
struct acpi_softc *acpi_sc;
|
||||
struct selinfo sel_read;
|
||||
};
|
||||
|
||||
#define ACPI_PRW_MAX_POWERRES 8
|
||||
|
||||
struct acpi_prw_data {
|
||||
@ -304,6 +327,8 @@ ACPI_STATUS acpi_AppendBufferResource(ACPI_BUFFER *buf,
|
||||
ACPI_RESOURCE *res);
|
||||
ACPI_STATUS acpi_OverrideInterruptLevel(UINT32 InterruptNumber);
|
||||
ACPI_STATUS acpi_SetIntrModel(int model);
|
||||
int acpi_ReqSleepState(struct acpi_softc *sc, int state);
|
||||
int acpi_AckSleepState(struct apm_clone_data *clone, int error);
|
||||
ACPI_STATUS acpi_SetSleepState(struct acpi_softc *sc, int state);
|
||||
int acpi_wake_init(device_t dev, int type);
|
||||
int acpi_wake_set_enable(device_t dev, int enable);
|
||||
|
@ -33,6 +33,8 @@ __FBSDID("$FreeBSD$");
|
||||
#include <sys/conf.h>
|
||||
#include <sys/fcntl.h>
|
||||
#include <sys/kernel.h>
|
||||
#include <sys/malloc.h>
|
||||
#include <sys/poll.h>
|
||||
#include <sys/sysctl.h>
|
||||
#include <sys/uio.h>
|
||||
#include <vm/vm.h>
|
||||
@ -46,8 +48,6 @@ __FBSDID("$FreeBSD$");
|
||||
* APM driver emulation
|
||||
*/
|
||||
|
||||
#include <sys/selinfo.h>
|
||||
|
||||
#include <machine/apm_bios.h>
|
||||
#include <machine/pc/bios.h>
|
||||
|
||||
@ -64,21 +64,31 @@ TUNABLE_INT("hw.acpi.reset_video", &acpi_reset_video);
|
||||
|
||||
static int intr_model = ACPI_INTR_PIC;
|
||||
static int apm_active;
|
||||
static struct clonedevs *apm_clones;
|
||||
|
||||
static d_open_t apmopen;
|
||||
static d_close_t apmclose;
|
||||
static d_write_t apmwrite;
|
||||
static d_ioctl_t apmioctl;
|
||||
static d_poll_t apmpoll;
|
||||
MALLOC_DEFINE(M_APMDEV, "apmdev", "APM device emulation");
|
||||
|
||||
static d_open_t apmopen;
|
||||
static d_close_t apmclose;
|
||||
static d_write_t apmwrite;
|
||||
static d_ioctl_t apmioctl;
|
||||
static d_poll_t apmpoll;
|
||||
static d_kqfilter_t apmkqfilter;
|
||||
static void apmreadfiltdetach(struct knote *kn);
|
||||
static int apmreadfilt(struct knote *kn, long hint);
|
||||
static struct filterops apm_readfiltops =
|
||||
{ 1, NULL, apmreadfiltdetach, apmreadfilt };
|
||||
|
||||
static struct cdevsw apm_cdevsw = {
|
||||
.d_version = D_VERSION,
|
||||
.d_flags = D_TRACKCLOSE,
|
||||
.d_open = apmopen,
|
||||
.d_close = apmclose,
|
||||
.d_write = apmwrite,
|
||||
.d_ioctl = apmioctl,
|
||||
.d_poll = apmpoll,
|
||||
.d_name = "apm",
|
||||
.d_kqfilter = apmkqfilter
|
||||
};
|
||||
|
||||
static int
|
||||
@ -202,44 +212,177 @@ acpi_capm_get_pwstatus(apm_pwstatus_t app)
|
||||
return (0);
|
||||
}
|
||||
|
||||
/* Create single-use devices for /dev/apm and /dev/apmctl. */
|
||||
static void
|
||||
apm_clone(void *arg, struct ucred *cred, char *name, int namelen,
|
||||
struct cdev **dev)
|
||||
{
|
||||
int ctl_dev, unit;
|
||||
|
||||
if (*dev != NULL)
|
||||
return;
|
||||
if (strcmp(name, "apmctl") == 0)
|
||||
ctl_dev = TRUE;
|
||||
else if (strcmp(name, "apm") == 0)
|
||||
ctl_dev = FALSE;
|
||||
else
|
||||
return;
|
||||
|
||||
/* Always create a new device and unit number. */
|
||||
unit = -1;
|
||||
if (clone_create(&apm_clones, &apm_cdevsw, &unit, dev, 0)) {
|
||||
if (ctl_dev) {
|
||||
*dev = make_dev(&apm_cdevsw, unit2minor(unit),
|
||||
UID_ROOT, GID_OPERATOR, 0660, "apmctl%d", unit);
|
||||
} else {
|
||||
*dev = make_dev(&apm_cdevsw, unit2minor(unit),
|
||||
UID_ROOT, GID_OPERATOR, 0664, "apm%d", unit);
|
||||
}
|
||||
if (*dev != NULL) {
|
||||
dev_ref(*dev);
|
||||
(*dev)->si_flags |= SI_CHEAPCLONE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Create a struct for tracking per-device suspend notification. */
|
||||
static struct apm_clone_data *
|
||||
apm_create_clone(struct cdev *dev, struct acpi_softc *acpi_sc)
|
||||
{
|
||||
struct apm_clone_data *clone;
|
||||
|
||||
clone = malloc(sizeof(*clone), M_APMDEV, M_WAITOK);
|
||||
clone->cdev = dev;
|
||||
clone->acpi_sc = acpi_sc;
|
||||
clone->notify_status = APM_EV_NONE;
|
||||
bzero(&clone->sel_read, sizeof(clone->sel_read));
|
||||
knlist_init(&clone->sel_read.si_note, &acpi_mutex, NULL, NULL, NULL);
|
||||
|
||||
/*
|
||||
* The acpi device is always managed by devd(8) and is considered
|
||||
* writable (i.e., ack is required to allow suspend to proceed.)
|
||||
*/
|
||||
if (strcmp("acpi", devtoname(dev)) == 0)
|
||||
clone->flags = ACPI_EVF_DEVD | ACPI_EVF_WRITE;
|
||||
else
|
||||
clone->flags = ACPI_EVF_NONE;
|
||||
|
||||
ACPI_LOCK(acpi);
|
||||
STAILQ_INSERT_TAIL(&acpi_sc->apm_cdevs, clone, entries);
|
||||
ACPI_UNLOCK(acpi);
|
||||
return (clone);
|
||||
}
|
||||
|
||||
/* XXX Kernel should be updated to allow calls to destroy_dev() in close(). */
|
||||
static void
|
||||
apm_destroy_clone(void *arg)
|
||||
{
|
||||
|
||||
destroy_dev((struct cdev *)arg);
|
||||
}
|
||||
|
||||
static int
|
||||
apmopen(struct cdev *dev, int flag, int fmt, d_thread_t *td)
|
||||
{
|
||||
struct acpi_softc *acpi_sc;
|
||||
struct apm_clone_data *clone;
|
||||
|
||||
acpi_sc = devclass_get_softc(devclass_find("acpi"), 0);
|
||||
clone = apm_create_clone(dev, acpi_sc);
|
||||
dev->si_drv1 = clone;
|
||||
|
||||
/* If the device is opened for write, record that. */
|
||||
if ((flag & FWRITE) != 0)
|
||||
clone->flags |= ACPI_EVF_WRITE;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
apmclose(struct cdev *dev, int flag, int fmt, d_thread_t *td)
|
||||
{
|
||||
struct apm_clone_data *clone;
|
||||
struct acpi_softc *acpi_sc;
|
||||
|
||||
clone = dev->si_drv1;
|
||||
acpi_sc = clone->acpi_sc;
|
||||
|
||||
/* We are about to lose a reference so check if suspend should occur */
|
||||
if (acpi_sc->acpi_next_sstate != 0 &&
|
||||
clone->notify_status != APM_EV_ACKED)
|
||||
acpi_AckSleepState(clone, 0);
|
||||
|
||||
/* Remove this clone's data from the list and free it. */
|
||||
ACPI_LOCK(acpi);
|
||||
STAILQ_REMOVE(&acpi_sc->apm_cdevs, clone, apm_clone_data, entries);
|
||||
knlist_destroy(&clone->sel_read.si_note);
|
||||
ACPI_UNLOCK(acpi);
|
||||
free(clone, M_APMDEV);
|
||||
AcpiOsExecute(OSL_GPE_HANDLER, apm_destroy_clone, dev);
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
apmioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flag, d_thread_t *td)
|
||||
{
|
||||
int error = 0;
|
||||
int error;
|
||||
struct apm_clone_data *clone;
|
||||
struct acpi_softc *acpi_sc;
|
||||
struct apm_info info;
|
||||
struct apm_info info;
|
||||
struct apm_event_info *ev_info;
|
||||
apm_info_old_t aiop;
|
||||
|
||||
acpi_sc = devclass_get_softc(devclass_find("acpi"), 0);
|
||||
error = 0;
|
||||
clone = dev->si_drv1;
|
||||
acpi_sc = clone->acpi_sc;
|
||||
|
||||
switch (cmd) {
|
||||
case APMIO_SUSPEND:
|
||||
if ((flag & FWRITE) == 0)
|
||||
return (EPERM);
|
||||
if (apm_active)
|
||||
acpi_SetSleepState(acpi_sc, acpi_sc->acpi_suspend_sx);
|
||||
else
|
||||
error = EINVAL;
|
||||
if (acpi_sc->acpi_next_sstate == 0) {
|
||||
if (acpi_sc->acpi_suspend_sx != ACPI_STATE_S5) {
|
||||
error = acpi_ReqSleepState(acpi_sc,
|
||||
acpi_sc->acpi_suspend_sx);
|
||||
} else {
|
||||
printf(
|
||||
"power off via apm suspend not supported\n");
|
||||
error = ENXIO;
|
||||
}
|
||||
} else
|
||||
error = acpi_AckSleepState(clone, 0);
|
||||
break;
|
||||
case APMIO_STANDBY:
|
||||
if ((flag & FWRITE) == 0)
|
||||
return (EPERM);
|
||||
if (apm_active)
|
||||
acpi_SetSleepState(acpi_sc, acpi_sc->acpi_standby_sx);
|
||||
else
|
||||
error = EINVAL;
|
||||
if (acpi_sc->acpi_next_sstate == 0) {
|
||||
if (acpi_sc->acpi_standby_sx != ACPI_STATE_S5) {
|
||||
error = acpi_ReqSleepState(acpi_sc,
|
||||
acpi_sc->acpi_standby_sx);
|
||||
} else {
|
||||
printf(
|
||||
"power off via apm standby not supported\n");
|
||||
error = ENXIO;
|
||||
}
|
||||
} else
|
||||
error = acpi_AckSleepState(clone, 0);
|
||||
break;
|
||||
case APMIO_NEXTEVENT:
|
||||
printf("apm nextevent start\n");
|
||||
ACPI_LOCK(acpi);
|
||||
if (acpi_sc->acpi_next_sstate != 0 && clone->notify_status ==
|
||||
APM_EV_NONE) {
|
||||
ev_info = (struct apm_event_info *)addr;
|
||||
if (acpi_sc->acpi_next_sstate <= ACPI_STATE_S3)
|
||||
ev_info->type = PMEV_STANDBYREQ;
|
||||
else
|
||||
ev_info->type = PMEV_SUSPENDREQ;
|
||||
ev_info->index = 0;
|
||||
clone->notify_status = APM_EV_NOTIFIED;
|
||||
printf("apm event returning %d\n", ev_info->type);
|
||||
} else
|
||||
error = EAGAIN;
|
||||
ACPI_UNLOCK(acpi);
|
||||
break;
|
||||
case APMIO_GETINFO_OLD:
|
||||
if (acpi_capm_get_info(&info))
|
||||
@ -300,24 +443,71 @@ apmwrite(struct cdev *dev, struct uio *uio, int ioflag)
|
||||
static int
|
||||
apmpoll(struct cdev *dev, int events, d_thread_t *td)
|
||||
{
|
||||
struct apm_clone_data *clone;
|
||||
int revents;
|
||||
|
||||
revents = 0;
|
||||
ACPI_LOCK(acpi);
|
||||
clone = dev->si_drv1;
|
||||
if (clone->acpi_sc->acpi_next_sstate)
|
||||
revents |= events & (POLLIN | POLLRDNORM);
|
||||
else
|
||||
selrecord(td, &clone->sel_read);
|
||||
ACPI_UNLOCK(acpi);
|
||||
return (revents);
|
||||
}
|
||||
|
||||
static int
|
||||
apmkqfilter(struct cdev *dev, struct knote *kn)
|
||||
{
|
||||
struct apm_clone_data *clone;
|
||||
|
||||
ACPI_LOCK(acpi);
|
||||
clone = dev->si_drv1;
|
||||
kn->kn_hook = clone;
|
||||
kn->kn_fop = &apm_readfiltops;
|
||||
knlist_add(&clone->sel_read.si_note, kn, 0);
|
||||
ACPI_UNLOCK(acpi);
|
||||
return (0);
|
||||
}
|
||||
|
||||
static void
|
||||
acpi_capm_init(struct acpi_softc *sc)
|
||||
apmreadfiltdetach(struct knote *kn)
|
||||
{
|
||||
make_dev(&apm_cdevsw, 0, 0, 5, 0664, "apm");
|
||||
struct apm_clone_data *clone;
|
||||
|
||||
ACPI_LOCK(acpi);
|
||||
clone = kn->kn_hook;
|
||||
knlist_remove(&clone->sel_read.si_note, kn, 0);
|
||||
ACPI_UNLOCK(acpi);
|
||||
}
|
||||
|
||||
static int
|
||||
apmreadfilt(struct knote *kn, long hint)
|
||||
{
|
||||
struct apm_clone_data *clone;
|
||||
int sleeping;
|
||||
|
||||
ACPI_LOCK(acpi);
|
||||
clone = kn->kn_hook;
|
||||
sleeping = clone->acpi_sc->acpi_next_sstate ? 1 : 0;
|
||||
ACPI_UNLOCK(acpi);
|
||||
return (sleeping);
|
||||
}
|
||||
|
||||
int
|
||||
acpi_machdep_init(device_t dev)
|
||||
{
|
||||
struct acpi_softc *sc;
|
||||
struct acpi_softc *acpi_sc;
|
||||
|
||||
sc = devclass_get_softc(devclass_find("acpi"), 0);
|
||||
acpi_capm_init(sc);
|
||||
acpi_sc = devclass_get_softc(devclass_find("acpi"), 0);
|
||||
|
||||
acpi_install_wakeup_handler(sc);
|
||||
/* Create a clone for /dev/acpi also. */
|
||||
STAILQ_INIT(&acpi_sc->apm_cdevs);
|
||||
acpi_sc->acpi_clone = apm_create_clone(acpi_sc->acpi_dev_t, acpi_sc);
|
||||
clone_setup(&apm_clones);
|
||||
EVENTHANDLER_REGISTER(dev_clone, apm_clone, 0, 1000);
|
||||
acpi_install_wakeup_handler(acpi_sc);
|
||||
|
||||
if (intr_model == ACPI_INTR_PIC)
|
||||
BUS_CONFIG_INTR(dev, AcpiGbl_FADT.SciInterrupt,
|
||||
@ -325,8 +515,8 @@ acpi_machdep_init(device_t dev)
|
||||
else
|
||||
acpi_SetIntrModel(intr_model);
|
||||
|
||||
SYSCTL_ADD_UINT(&sc->acpi_sysctl_ctx,
|
||||
SYSCTL_CHILDREN(sc->acpi_sysctl_tree), OID_AUTO,
|
||||
SYSCTL_ADD_UINT(&acpi_sc->acpi_sysctl_ctx,
|
||||
SYSCTL_CHILDREN(acpi_sc->acpi_sysctl_tree), OID_AUTO,
|
||||
"reset_video", CTLFLAG_RW, &acpi_reset_video, 0,
|
||||
"Call the VESA reset BIOS vector on the resume path");
|
||||
|
||||
|
@ -40,8 +40,6 @@
|
||||
#include <contrib/dev/acpica/acpi.h>
|
||||
|
||||
#define ACPIDEV "/dev/acpi"
|
||||
#define RC_SUSPEND_PATH "/etc/rc.suspend"
|
||||
#define RC_RESUME_PATH "/etc/rc.resume"
|
||||
|
||||
static int acpifd;
|
||||
|
||||
@ -55,32 +53,27 @@ acpi_init(void)
|
||||
err(EX_OSFILE, ACPIDEV);
|
||||
}
|
||||
|
||||
static int
|
||||
/* Prepare to sleep and then wait for the signal that sleeping can occur. */
|
||||
static void
|
||||
acpi_sleep(int sleep_type)
|
||||
{
|
||||
char cmd[64];
|
||||
int ret;
|
||||
|
||||
/* Run the suspend rc script, if available. */
|
||||
if (access(RC_SUSPEND_PATH, X_OK) == 0) {
|
||||
snprintf(cmd, sizeof(cmd), "%s acpi %d", RC_SUSPEND_PATH,
|
||||
sleep_type);
|
||||
system(cmd);
|
||||
}
|
||||
|
||||
ret = ioctl(acpifd, ACPIIO_SETSLPSTATE, &sleep_type);
|
||||
|
||||
/* Run the resume rc script, if available. */
|
||||
if (access(RC_RESUME_PATH, X_OK) == 0) {
|
||||
snprintf(cmd, sizeof(cmd), "%s acpi %d", RC_RESUME_PATH,
|
||||
sleep_type);
|
||||
system(cmd);
|
||||
}
|
||||
|
||||
/* Notify OS that we want to sleep. devd(8) gets this notify. */
|
||||
ret = ioctl(acpifd, ACPIIO_REQSLPSTATE, &sleep_type);
|
||||
if (ret != 0)
|
||||
err(EX_IOERR, "sleep type (%d) failed", sleep_type);
|
||||
err(EX_IOERR, "request sleep type (%d) failed", sleep_type);
|
||||
}
|
||||
|
||||
return (0);
|
||||
/* Ack or abort a pending suspend request. */
|
||||
static void
|
||||
acpi_sleep_ack(int err_val)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = ioctl(acpifd, ACPIIO_ACKSLPSTATE, &err_val);
|
||||
if (ret != 0)
|
||||
err(EX_IOERR, "ack sleep type failed");
|
||||
}
|
||||
|
||||
/* should be a acpi define, but doesn't appear to be */
|
||||
@ -183,7 +176,7 @@ acpi_battinfo(int num)
|
||||
static void
|
||||
usage(const char* prog)
|
||||
{
|
||||
printf("usage: %s [-h] [-i batt] [-s 1-5]\n", prog);
|
||||
printf("usage: %s [-h] [-i batt] [-k ack] [-s 1-4]\n", prog);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
@ -200,17 +193,20 @@ main(int argc, char *argv[])
|
||||
|
||||
sleep_type = -1;
|
||||
acpi_init();
|
||||
while ((c = getopt(argc, argv, "hi:s:")) != -1) {
|
||||
while ((c = getopt(argc, argv, "hi:k:s:")) != -1) {
|
||||
switch (c) {
|
||||
case 'i':
|
||||
acpi_battinfo(atoi(optarg));
|
||||
break;
|
||||
case 'k':
|
||||
acpi_sleep_ack(atoi(optarg));
|
||||
break;
|
||||
case 's':
|
||||
if (optarg[0] == 'S')
|
||||
sleep_type = optarg[1] - '0';
|
||||
else
|
||||
sleep_type = optarg[0] - '0';
|
||||
if (sleep_type < 0 || sleep_type > 5)
|
||||
if (sleep_type < 1 || sleep_type > 4)
|
||||
errx(EX_USAGE, "invalid sleep type (%d)",
|
||||
sleep_type);
|
||||
break;
|
||||
@ -223,10 +219,8 @@ main(int argc, char *argv[])
|
||||
argc -= optind;
|
||||
argv += optind;
|
||||
|
||||
if (sleep_type != -1) {
|
||||
sleep(1); /* wait 1 sec. for key-release event */
|
||||
if (sleep_type != -1)
|
||||
acpi_sleep(sleep_type);
|
||||
}
|
||||
|
||||
close(acpifd);
|
||||
exit (0);
|
||||
|
Loading…
Reference in New Issue
Block a user