Add apmd support code.
This commit is contained in:
parent
be16db802f
commit
346d4b28b9
@ -15,7 +15,7 @@
|
|||||||
*
|
*
|
||||||
* Sep, 1994 Implemented on FreeBSD 1.1.5.1R (Toshiba AVS001WD)
|
* Sep, 1994 Implemented on FreeBSD 1.1.5.1R (Toshiba AVS001WD)
|
||||||
*
|
*
|
||||||
* $Id: apm.c,v 1.88 1999/06/01 18:17:50 jlemon Exp $
|
* $Id: apm.c,v 1.89 1999/07/04 14:58:29 phk Exp $
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "opt_devfs.h"
|
#include "opt_devfs.h"
|
||||||
@ -31,6 +31,12 @@
|
|||||||
#include <sys/time.h>
|
#include <sys/time.h>
|
||||||
#include <sys/reboot.h>
|
#include <sys/reboot.h>
|
||||||
#include <sys/bus.h>
|
#include <sys/bus.h>
|
||||||
|
#include <sys/select.h>
|
||||||
|
#include <sys/poll.h>
|
||||||
|
#include <sys/fcntl.h>
|
||||||
|
#include <sys/proc.h>
|
||||||
|
#include <sys/uio.h>
|
||||||
|
#include <sys/signalvar.h>
|
||||||
#include <machine/apm_bios.h>
|
#include <machine/apm_bios.h>
|
||||||
#include <machine/segments.h>
|
#include <machine/segments.h>
|
||||||
#include <machine/clock.h>
|
#include <machine/clock.h>
|
||||||
@ -51,6 +57,11 @@ static int apm_display __P((int newstate));
|
|||||||
static int apm_int __P((u_long *eax, u_long *ebx, u_long *ecx, u_long *edx));
|
static int apm_int __P((u_long *eax, u_long *ebx, u_long *ecx, u_long *edx));
|
||||||
static void apm_resume __P((void));
|
static void apm_resume __P((void));
|
||||||
|
|
||||||
|
#define APM_NEVENTS 16
|
||||||
|
#define APM_NPMEV 13
|
||||||
|
|
||||||
|
int apm_evindex;
|
||||||
|
|
||||||
/* static data */
|
/* static data */
|
||||||
struct apm_softc {
|
struct apm_softc {
|
||||||
int initialized, active;
|
int initialized, active;
|
||||||
@ -63,10 +74,23 @@ struct apm_softc {
|
|||||||
u_int intversion;
|
u_int intversion;
|
||||||
struct apmhook sc_suspend;
|
struct apmhook sc_suspend;
|
||||||
struct apmhook sc_resume;
|
struct apmhook sc_resume;
|
||||||
|
struct selinfo sc_rsel;
|
||||||
|
int sc_flags;
|
||||||
|
int event_count;
|
||||||
|
int event_ptr;
|
||||||
|
struct apm_event_info event_list[APM_NEVENTS];
|
||||||
|
u_char event_filter[APM_NPMEV];
|
||||||
#ifdef DEVFS
|
#ifdef DEVFS
|
||||||
void *sc_devfs_token;
|
void *sc_devfs_token;
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
#define SCFLAG_ONORMAL 0x0000001
|
||||||
|
#define SCFLAG_OCTL 0x0000002
|
||||||
|
#define SCFLAG_OPEN (SCFLAG_ONORMAL|SCFLAG_OCTL)
|
||||||
|
|
||||||
|
#define APMDEV(dev) (minor(dev)&0x0f)
|
||||||
|
#define APMDEV_NORMAL 0
|
||||||
|
#define APMDEV_CTL 8
|
||||||
|
|
||||||
static struct apm_softc apm_softc;
|
static struct apm_softc apm_softc;
|
||||||
static struct apmhook *hook[NAPM_HOOK]; /* XXX */
|
static struct apmhook *hook[NAPM_HOOK]; /* XXX */
|
||||||
@ -82,19 +106,21 @@ static struct callout_handle apm_timeout_ch =
|
|||||||
static timeout_t apm_timeout;
|
static timeout_t apm_timeout;
|
||||||
static d_open_t apmopen;
|
static d_open_t apmopen;
|
||||||
static d_close_t apmclose;
|
static d_close_t apmclose;
|
||||||
|
static d_write_t apmwrite;
|
||||||
static d_ioctl_t apmioctl;
|
static d_ioctl_t apmioctl;
|
||||||
|
static d_poll_t apmpoll;
|
||||||
|
|
||||||
#define CDEV_MAJOR 39
|
#define CDEV_MAJOR 39
|
||||||
static struct cdevsw apm_cdevsw = {
|
static struct cdevsw apm_cdevsw = {
|
||||||
/* open */ apmopen,
|
/* open */ apmopen,
|
||||||
/* close */ apmclose,
|
/* close */ apmclose,
|
||||||
/* read */ noread,
|
/* read */ noread,
|
||||||
/* write */ nowrite,
|
/* write */ apmwrite,
|
||||||
/* ioctl */ apmioctl,
|
/* ioctl */ apmioctl,
|
||||||
/* stop */ nostop,
|
/* stop */ nostop,
|
||||||
/* reset */ noreset,
|
/* reset */ noreset,
|
||||||
/* devtotty */ nodevtotty,
|
/* devtotty */ nodevtotty,
|
||||||
/* poll */ nopoll,
|
/* poll */ apmpoll,
|
||||||
/* mmap */ nommap,
|
/* mmap */ nommap,
|
||||||
/* strategy */ nostrategy,
|
/* strategy */ nostrategy,
|
||||||
/* name */ "apm",
|
/* name */ "apm",
|
||||||
@ -450,8 +476,50 @@ apm_default_suspend(void *arg)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int apm_record_event __P((struct apm_softc *, u_int));
|
||||||
static void apm_processevent(void);
|
static void apm_processevent(void);
|
||||||
|
|
||||||
|
static u_int apm_op_inprog = 0;
|
||||||
|
|
||||||
|
static void
|
||||||
|
apm_lastreq_notify(void)
|
||||||
|
{
|
||||||
|
u_long eax, ebx, ecx, edx;
|
||||||
|
|
||||||
|
eax = (APM_BIOS << 8) | APM_SETPWSTATE;
|
||||||
|
ebx = PMDV_ALLDEV;
|
||||||
|
ecx = PMST_LASTREQNOTIFY;
|
||||||
|
edx = 0;
|
||||||
|
|
||||||
|
apm_int(&eax, &ebx, &ecx, &edx);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
apm_lastreq_rejected(void)
|
||||||
|
{
|
||||||
|
u_long eax, ebx, ecx, edx;
|
||||||
|
|
||||||
|
if (apm_op_inprog == 0) {
|
||||||
|
return 1; /* no operation in progress */
|
||||||
|
}
|
||||||
|
|
||||||
|
eax = (APM_BIOS << 8) | APM_SETPWSTATE;
|
||||||
|
ebx = PMDV_ALLDEV;
|
||||||
|
ecx = PMST_LASTREQREJECT;
|
||||||
|
edx = 0;
|
||||||
|
|
||||||
|
if (apm_int(&eax, &ebx, &ecx, &edx)) {
|
||||||
|
#ifdef APM_DEBUG
|
||||||
|
printf("apm_lastreq_rejected: failed\n");
|
||||||
|
#endif
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
apm_op_inprog = 0;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Public interface to the suspend/resume:
|
* Public interface to the suspend/resume:
|
||||||
*
|
*
|
||||||
@ -468,6 +536,8 @@ apm_suspend(int state)
|
|||||||
if (!sc)
|
if (!sc)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
apm_op_inprog = 0;
|
||||||
|
|
||||||
if (sc->initialized) {
|
if (sc->initialized) {
|
||||||
error = DEVICE_SUSPEND(root_bus);
|
error = DEVICE_SUSPEND(root_bus);
|
||||||
/*
|
/*
|
||||||
@ -608,7 +678,11 @@ apm_timeout(void *dummy)
|
|||||||
{
|
{
|
||||||
struct apm_softc *sc = &apm_softc;
|
struct apm_softc *sc = &apm_softc;
|
||||||
|
|
||||||
|
if (apm_op_inprog)
|
||||||
|
apm_lastreq_notify();
|
||||||
|
|
||||||
apm_processevent();
|
apm_processevent();
|
||||||
|
|
||||||
if (sc->active == 1)
|
if (sc->active == 1)
|
||||||
/* Run slightly more oftan than 1 Hz */
|
/* Run slightly more oftan than 1 Hz */
|
||||||
apm_timeout_ch = timeout(apm_timeout, NULL, hz - 1 );
|
apm_timeout_ch = timeout(apm_timeout, NULL, hz - 1 );
|
||||||
@ -762,12 +836,37 @@ apm_probe(device_t dev)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* return 0 if the user will notice and handle the event,
|
||||||
|
* return 1 if the kernel driver should do so.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
apm_record_event(struct apm_softc *sc, u_int event_type)
|
||||||
|
{
|
||||||
|
struct apm_event_info *evp;
|
||||||
|
|
||||||
|
if ((sc->sc_flags & SCFLAG_OPEN) == 0)
|
||||||
|
return 1; /* no user waiting */
|
||||||
|
if (sc->event_count == APM_NEVENTS)
|
||||||
|
return 1; /* overflow */
|
||||||
|
if (sc->event_filter[event_type] == 0)
|
||||||
|
return 1; /* not registered */
|
||||||
|
evp = &sc->event_list[sc->event_ptr];
|
||||||
|
sc->event_count++;
|
||||||
|
sc->event_ptr++;
|
||||||
|
sc->event_ptr %= APM_NEVENTS;
|
||||||
|
evp->type = event_type;
|
||||||
|
evp->index = ++apm_evindex;
|
||||||
|
selwakeup(&sc->sc_rsel);
|
||||||
|
return (sc->sc_flags & SCFLAG_OCTL) ? 0 : 1; /* user may handle */
|
||||||
|
}
|
||||||
|
|
||||||
/* Process APM event */
|
/* Process APM event */
|
||||||
static void
|
static void
|
||||||
apm_processevent(void)
|
apm_processevent(void)
|
||||||
{
|
{
|
||||||
int apm_event;
|
int apm_event;
|
||||||
|
struct apm_softc *sc = &apm_softc;
|
||||||
|
|
||||||
#ifdef APM_DEBUG
|
#ifdef APM_DEBUG
|
||||||
# define OPMEV_DEBUGMESSAGE(symbol) case symbol: \
|
# define OPMEV_DEBUGMESSAGE(symbol) case symbol: \
|
||||||
@ -779,33 +878,57 @@ apm_processevent(void)
|
|||||||
apm_event = apm_getevent();
|
apm_event = apm_getevent();
|
||||||
switch (apm_event) {
|
switch (apm_event) {
|
||||||
OPMEV_DEBUGMESSAGE(PMEV_STANDBYREQ);
|
OPMEV_DEBUGMESSAGE(PMEV_STANDBYREQ);
|
||||||
apm_suspend(PMST_STANDBY);
|
if (apm_op_inprog == 0) {
|
||||||
|
apm_op_inprog++;
|
||||||
|
if (apm_record_event(sc, apm_event)) {
|
||||||
|
apm_suspend(PMST_STANDBY);
|
||||||
|
}
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
OPMEV_DEBUGMESSAGE(PMEV_SUSPENDREQ);
|
OPMEV_DEBUGMESSAGE(PMEV_SUSPENDREQ);
|
||||||
apm_suspend(PMST_SUSPEND);
|
apm_lastreq_notify();
|
||||||
break;
|
if (apm_op_inprog == 0) {
|
||||||
|
apm_op_inprog++;
|
||||||
|
if (apm_record_event(sc, apm_event)) {
|
||||||
|
apm_suspend(PMST_SUSPEND);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return; /* XXX skip the rest */
|
||||||
OPMEV_DEBUGMESSAGE(PMEV_USERSUSPENDREQ);
|
OPMEV_DEBUGMESSAGE(PMEV_USERSUSPENDREQ);
|
||||||
apm_suspend(PMST_SUSPEND);
|
apm_lastreq_notify();
|
||||||
break;
|
if (apm_op_inprog == 0) {
|
||||||
|
apm_op_inprog++;
|
||||||
|
if (apm_record_event(sc, apm_event)) {
|
||||||
|
apm_suspend(PMST_SUSPEND);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return; /* XXX skip the rest */
|
||||||
OPMEV_DEBUGMESSAGE(PMEV_CRITSUSPEND);
|
OPMEV_DEBUGMESSAGE(PMEV_CRITSUSPEND);
|
||||||
apm_suspend(PMST_SUSPEND);
|
apm_suspend(PMST_SUSPEND);
|
||||||
break;
|
break;
|
||||||
OPMEV_DEBUGMESSAGE(PMEV_NORMRESUME);
|
OPMEV_DEBUGMESSAGE(PMEV_NORMRESUME);
|
||||||
|
apm_record_event(sc, apm_event);
|
||||||
apm_resume();
|
apm_resume();
|
||||||
break;
|
break;
|
||||||
OPMEV_DEBUGMESSAGE(PMEV_CRITRESUME);
|
OPMEV_DEBUGMESSAGE(PMEV_CRITRESUME);
|
||||||
|
apm_record_event(sc, apm_event);
|
||||||
apm_resume();
|
apm_resume();
|
||||||
break;
|
break;
|
||||||
OPMEV_DEBUGMESSAGE(PMEV_STANDBYRESUME);
|
OPMEV_DEBUGMESSAGE(PMEV_STANDBYRESUME);
|
||||||
|
apm_record_event(sc, apm_event);
|
||||||
apm_resume();
|
apm_resume();
|
||||||
break;
|
break;
|
||||||
OPMEV_DEBUGMESSAGE(PMEV_BATTERYLOW);
|
OPMEV_DEBUGMESSAGE(PMEV_BATTERYLOW);
|
||||||
apm_battery_low();
|
if (apm_record_event(sc, apm_event)) {
|
||||||
apm_suspend(PMST_SUSPEND);
|
apm_battery_low();
|
||||||
|
apm_suspend(PMST_SUSPEND);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
OPMEV_DEBUGMESSAGE(PMEV_POWERSTATECHANGE);
|
OPMEV_DEBUGMESSAGE(PMEV_POWERSTATECHANGE);
|
||||||
|
apm_record_event(sc, apm_event);
|
||||||
break;
|
break;
|
||||||
OPMEV_DEBUGMESSAGE(PMEV_UPDATETIME);
|
OPMEV_DEBUGMESSAGE(PMEV_UPDATETIME);
|
||||||
|
apm_record_event(sc, apm_event);
|
||||||
inittodr(0); /* adjust time to RTC */
|
inittodr(0); /* adjust time to RTC */
|
||||||
break;
|
break;
|
||||||
case PMEV_NOEVENT:
|
case PMEV_NOEVENT:
|
||||||
@ -970,16 +1093,50 @@ static int
|
|||||||
apmopen(dev_t dev, int flag, int fmt, struct proc *p)
|
apmopen(dev_t dev, int flag, int fmt, struct proc *p)
|
||||||
{
|
{
|
||||||
struct apm_softc *sc = &apm_softc;
|
struct apm_softc *sc = &apm_softc;
|
||||||
|
int ctl = APMDEV(dev);
|
||||||
|
|
||||||
if (minor(dev) != 0 || !sc->initialized)
|
if (!sc->initialized)
|
||||||
return (ENXIO);
|
return (ENXIO);
|
||||||
|
|
||||||
|
switch (ctl) {
|
||||||
|
case APMDEV_CTL:
|
||||||
|
if (!(flag & FWRITE))
|
||||||
|
return EINVAL;
|
||||||
|
if (sc->sc_flags & SCFLAG_OCTL)
|
||||||
|
return EBUSY;
|
||||||
|
sc->sc_flags |= SCFLAG_OCTL;
|
||||||
|
bzero(sc->event_filter, sizeof sc->event_filter);
|
||||||
|
break;
|
||||||
|
case APMDEV_NORMAL:
|
||||||
|
sc->sc_flags |= SCFLAG_ONORMAL;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return ENXIO;
|
||||||
|
break;
|
||||||
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
apmclose(dev_t dev, int flag, int fmt, struct proc *p)
|
apmclose(dev_t dev, int flag, int fmt, struct proc *p)
|
||||||
{
|
{
|
||||||
|
struct apm_softc *sc = &apm_softc;
|
||||||
|
int ctl = APMDEV(dev);
|
||||||
|
|
||||||
|
switch (ctl) {
|
||||||
|
case APMDEV_CTL:
|
||||||
|
apm_lastreq_rejected();
|
||||||
|
sc->sc_flags &= ~SCFLAG_OCTL;
|
||||||
|
bzero(sc->event_filter, sizeof sc->event_filter);
|
||||||
|
break;
|
||||||
|
case APMDEV_NORMAL:
|
||||||
|
sc->sc_flags &= ~SCFLAG_ONORMAL;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if ((sc->sc_flags & SCFLAG_OPEN) == 0) {
|
||||||
|
sc->event_count = 0;
|
||||||
|
sc->event_ptr = 0;
|
||||||
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -990,7 +1147,7 @@ apmioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, struct proc *p)
|
|||||||
int error = 0;
|
int error = 0;
|
||||||
int newstate;
|
int newstate;
|
||||||
|
|
||||||
if (minor(dev) != 0 || !sc->initialized)
|
if (!sc->initialized)
|
||||||
return (ENXIO);
|
return (ENXIO);
|
||||||
#ifdef APM_DEBUG
|
#ifdef APM_DEBUG
|
||||||
printf("APM ioctl: cmd = 0x%x\n", cmd);
|
printf("APM ioctl: cmd = 0x%x\n", cmd);
|
||||||
@ -1055,9 +1212,86 @@ apmioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, struct proc *p)
|
|||||||
error = EINVAL;
|
error = EINVAL;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* for /dev/apmctl */
|
||||||
|
if (APMDEV(dev) == APMDEV_CTL) {
|
||||||
|
struct apm_event_info *evp;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
error = 0;
|
||||||
|
switch (cmd) {
|
||||||
|
case APMIO_NEXTEVENT:
|
||||||
|
if (!sc->event_count) {
|
||||||
|
error = EAGAIN;
|
||||||
|
} else {
|
||||||
|
evp = (struct apm_event_info *)addr;
|
||||||
|
i = sc->event_ptr + APM_NEVENTS - sc->event_count;
|
||||||
|
i %= APM_NEVENTS;
|
||||||
|
*evp = sc->event_list[i];
|
||||||
|
sc->event_count--;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case APMIO_REJECTLASTREQ:
|
||||||
|
if (apm_lastreq_rejected()) {
|
||||||
|
error = EINVAL;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
error = EINVAL;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
apmwrite(dev_t dev, struct uio *uio, int ioflag)
|
||||||
|
{
|
||||||
|
struct apm_softc *sc = &apm_softc;
|
||||||
|
u_int event_type;
|
||||||
|
int error;
|
||||||
|
u_char enabled;
|
||||||
|
|
||||||
|
if (APMDEV(dev) != APMDEV_CTL)
|
||||||
|
return(ENODEV);
|
||||||
|
if (uio->uio_resid != sizeof(u_int))
|
||||||
|
return(E2BIG);
|
||||||
|
|
||||||
|
if ((error = uiomove((caddr_t)&event_type, sizeof(u_int), uio)))
|
||||||
|
return(error);
|
||||||
|
|
||||||
|
if (event_type < 0 || event_type >= APM_NPMEV)
|
||||||
|
return(EINVAL);
|
||||||
|
|
||||||
|
if (sc->event_filter[event_type] == 0) {
|
||||||
|
enabled = 1;
|
||||||
|
} else {
|
||||||
|
enabled = 0;
|
||||||
|
}
|
||||||
|
sc->event_filter[event_type] = enabled;
|
||||||
|
#ifdef APM_DEBUG
|
||||||
|
printf("apmwrite: event 0x%x %s\n", event_type, is_enabled(enabled));
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return uio->uio_resid;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
apmpoll(dev_t dev, int events, struct proc *p)
|
||||||
|
{
|
||||||
|
struct apm_softc *sc = &apm_softc;
|
||||||
|
int revents = 0;
|
||||||
|
|
||||||
|
if (events & (POLLIN | POLLRDNORM))
|
||||||
|
if (sc->event_count)
|
||||||
|
revents |= events & (POLLIN | POLLRDNORM);
|
||||||
|
else
|
||||||
|
selrecord(p, &sc->sc_rsel);
|
||||||
|
|
||||||
|
return (revents);
|
||||||
|
}
|
||||||
|
|
||||||
static device_method_t apm_methods[] = {
|
static device_method_t apm_methods[] = {
|
||||||
/* Device interface */
|
/* Device interface */
|
||||||
DEVMETHOD(device_probe, apm_probe),
|
DEVMETHOD(device_probe, apm_probe),
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
*
|
*
|
||||||
* Sep, 1994 Implemented on FreeBSD 1.1.5.1R (Toshiba AVS001WD)
|
* Sep, 1994 Implemented on FreeBSD 1.1.5.1R (Toshiba AVS001WD)
|
||||||
*
|
*
|
||||||
* $Id: apm.c,v 1.88 1999/06/01 18:17:50 jlemon Exp $
|
* $Id: apm.c,v 1.89 1999/07/04 14:58:29 phk Exp $
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "opt_devfs.h"
|
#include "opt_devfs.h"
|
||||||
@ -31,6 +31,12 @@
|
|||||||
#include <sys/time.h>
|
#include <sys/time.h>
|
||||||
#include <sys/reboot.h>
|
#include <sys/reboot.h>
|
||||||
#include <sys/bus.h>
|
#include <sys/bus.h>
|
||||||
|
#include <sys/select.h>
|
||||||
|
#include <sys/poll.h>
|
||||||
|
#include <sys/fcntl.h>
|
||||||
|
#include <sys/proc.h>
|
||||||
|
#include <sys/uio.h>
|
||||||
|
#include <sys/signalvar.h>
|
||||||
#include <machine/apm_bios.h>
|
#include <machine/apm_bios.h>
|
||||||
#include <machine/segments.h>
|
#include <machine/segments.h>
|
||||||
#include <machine/clock.h>
|
#include <machine/clock.h>
|
||||||
@ -51,6 +57,11 @@ static int apm_display __P((int newstate));
|
|||||||
static int apm_int __P((u_long *eax, u_long *ebx, u_long *ecx, u_long *edx));
|
static int apm_int __P((u_long *eax, u_long *ebx, u_long *ecx, u_long *edx));
|
||||||
static void apm_resume __P((void));
|
static void apm_resume __P((void));
|
||||||
|
|
||||||
|
#define APM_NEVENTS 16
|
||||||
|
#define APM_NPMEV 13
|
||||||
|
|
||||||
|
int apm_evindex;
|
||||||
|
|
||||||
/* static data */
|
/* static data */
|
||||||
struct apm_softc {
|
struct apm_softc {
|
||||||
int initialized, active;
|
int initialized, active;
|
||||||
@ -63,10 +74,23 @@ struct apm_softc {
|
|||||||
u_int intversion;
|
u_int intversion;
|
||||||
struct apmhook sc_suspend;
|
struct apmhook sc_suspend;
|
||||||
struct apmhook sc_resume;
|
struct apmhook sc_resume;
|
||||||
|
struct selinfo sc_rsel;
|
||||||
|
int sc_flags;
|
||||||
|
int event_count;
|
||||||
|
int event_ptr;
|
||||||
|
struct apm_event_info event_list[APM_NEVENTS];
|
||||||
|
u_char event_filter[APM_NPMEV];
|
||||||
#ifdef DEVFS
|
#ifdef DEVFS
|
||||||
void *sc_devfs_token;
|
void *sc_devfs_token;
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
#define SCFLAG_ONORMAL 0x0000001
|
||||||
|
#define SCFLAG_OCTL 0x0000002
|
||||||
|
#define SCFLAG_OPEN (SCFLAG_ONORMAL|SCFLAG_OCTL)
|
||||||
|
|
||||||
|
#define APMDEV(dev) (minor(dev)&0x0f)
|
||||||
|
#define APMDEV_NORMAL 0
|
||||||
|
#define APMDEV_CTL 8
|
||||||
|
|
||||||
static struct apm_softc apm_softc;
|
static struct apm_softc apm_softc;
|
||||||
static struct apmhook *hook[NAPM_HOOK]; /* XXX */
|
static struct apmhook *hook[NAPM_HOOK]; /* XXX */
|
||||||
@ -82,19 +106,21 @@ static struct callout_handle apm_timeout_ch =
|
|||||||
static timeout_t apm_timeout;
|
static timeout_t apm_timeout;
|
||||||
static d_open_t apmopen;
|
static d_open_t apmopen;
|
||||||
static d_close_t apmclose;
|
static d_close_t apmclose;
|
||||||
|
static d_write_t apmwrite;
|
||||||
static d_ioctl_t apmioctl;
|
static d_ioctl_t apmioctl;
|
||||||
|
static d_poll_t apmpoll;
|
||||||
|
|
||||||
#define CDEV_MAJOR 39
|
#define CDEV_MAJOR 39
|
||||||
static struct cdevsw apm_cdevsw = {
|
static struct cdevsw apm_cdevsw = {
|
||||||
/* open */ apmopen,
|
/* open */ apmopen,
|
||||||
/* close */ apmclose,
|
/* close */ apmclose,
|
||||||
/* read */ noread,
|
/* read */ noread,
|
||||||
/* write */ nowrite,
|
/* write */ apmwrite,
|
||||||
/* ioctl */ apmioctl,
|
/* ioctl */ apmioctl,
|
||||||
/* stop */ nostop,
|
/* stop */ nostop,
|
||||||
/* reset */ noreset,
|
/* reset */ noreset,
|
||||||
/* devtotty */ nodevtotty,
|
/* devtotty */ nodevtotty,
|
||||||
/* poll */ nopoll,
|
/* poll */ apmpoll,
|
||||||
/* mmap */ nommap,
|
/* mmap */ nommap,
|
||||||
/* strategy */ nostrategy,
|
/* strategy */ nostrategy,
|
||||||
/* name */ "apm",
|
/* name */ "apm",
|
||||||
@ -450,8 +476,50 @@ apm_default_suspend(void *arg)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int apm_record_event __P((struct apm_softc *, u_int));
|
||||||
static void apm_processevent(void);
|
static void apm_processevent(void);
|
||||||
|
|
||||||
|
static u_int apm_op_inprog = 0;
|
||||||
|
|
||||||
|
static void
|
||||||
|
apm_lastreq_notify(void)
|
||||||
|
{
|
||||||
|
u_long eax, ebx, ecx, edx;
|
||||||
|
|
||||||
|
eax = (APM_BIOS << 8) | APM_SETPWSTATE;
|
||||||
|
ebx = PMDV_ALLDEV;
|
||||||
|
ecx = PMST_LASTREQNOTIFY;
|
||||||
|
edx = 0;
|
||||||
|
|
||||||
|
apm_int(&eax, &ebx, &ecx, &edx);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
apm_lastreq_rejected(void)
|
||||||
|
{
|
||||||
|
u_long eax, ebx, ecx, edx;
|
||||||
|
|
||||||
|
if (apm_op_inprog == 0) {
|
||||||
|
return 1; /* no operation in progress */
|
||||||
|
}
|
||||||
|
|
||||||
|
eax = (APM_BIOS << 8) | APM_SETPWSTATE;
|
||||||
|
ebx = PMDV_ALLDEV;
|
||||||
|
ecx = PMST_LASTREQREJECT;
|
||||||
|
edx = 0;
|
||||||
|
|
||||||
|
if (apm_int(&eax, &ebx, &ecx, &edx)) {
|
||||||
|
#ifdef APM_DEBUG
|
||||||
|
printf("apm_lastreq_rejected: failed\n");
|
||||||
|
#endif
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
apm_op_inprog = 0;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Public interface to the suspend/resume:
|
* Public interface to the suspend/resume:
|
||||||
*
|
*
|
||||||
@ -468,6 +536,8 @@ apm_suspend(int state)
|
|||||||
if (!sc)
|
if (!sc)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
apm_op_inprog = 0;
|
||||||
|
|
||||||
if (sc->initialized) {
|
if (sc->initialized) {
|
||||||
error = DEVICE_SUSPEND(root_bus);
|
error = DEVICE_SUSPEND(root_bus);
|
||||||
/*
|
/*
|
||||||
@ -608,7 +678,11 @@ apm_timeout(void *dummy)
|
|||||||
{
|
{
|
||||||
struct apm_softc *sc = &apm_softc;
|
struct apm_softc *sc = &apm_softc;
|
||||||
|
|
||||||
|
if (apm_op_inprog)
|
||||||
|
apm_lastreq_notify();
|
||||||
|
|
||||||
apm_processevent();
|
apm_processevent();
|
||||||
|
|
||||||
if (sc->active == 1)
|
if (sc->active == 1)
|
||||||
/* Run slightly more oftan than 1 Hz */
|
/* Run slightly more oftan than 1 Hz */
|
||||||
apm_timeout_ch = timeout(apm_timeout, NULL, hz - 1 );
|
apm_timeout_ch = timeout(apm_timeout, NULL, hz - 1 );
|
||||||
@ -762,12 +836,37 @@ apm_probe(device_t dev)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* return 0 if the user will notice and handle the event,
|
||||||
|
* return 1 if the kernel driver should do so.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
apm_record_event(struct apm_softc *sc, u_int event_type)
|
||||||
|
{
|
||||||
|
struct apm_event_info *evp;
|
||||||
|
|
||||||
|
if ((sc->sc_flags & SCFLAG_OPEN) == 0)
|
||||||
|
return 1; /* no user waiting */
|
||||||
|
if (sc->event_count == APM_NEVENTS)
|
||||||
|
return 1; /* overflow */
|
||||||
|
if (sc->event_filter[event_type] == 0)
|
||||||
|
return 1; /* not registered */
|
||||||
|
evp = &sc->event_list[sc->event_ptr];
|
||||||
|
sc->event_count++;
|
||||||
|
sc->event_ptr++;
|
||||||
|
sc->event_ptr %= APM_NEVENTS;
|
||||||
|
evp->type = event_type;
|
||||||
|
evp->index = ++apm_evindex;
|
||||||
|
selwakeup(&sc->sc_rsel);
|
||||||
|
return (sc->sc_flags & SCFLAG_OCTL) ? 0 : 1; /* user may handle */
|
||||||
|
}
|
||||||
|
|
||||||
/* Process APM event */
|
/* Process APM event */
|
||||||
static void
|
static void
|
||||||
apm_processevent(void)
|
apm_processevent(void)
|
||||||
{
|
{
|
||||||
int apm_event;
|
int apm_event;
|
||||||
|
struct apm_softc *sc = &apm_softc;
|
||||||
|
|
||||||
#ifdef APM_DEBUG
|
#ifdef APM_DEBUG
|
||||||
# define OPMEV_DEBUGMESSAGE(symbol) case symbol: \
|
# define OPMEV_DEBUGMESSAGE(symbol) case symbol: \
|
||||||
@ -779,33 +878,57 @@ apm_processevent(void)
|
|||||||
apm_event = apm_getevent();
|
apm_event = apm_getevent();
|
||||||
switch (apm_event) {
|
switch (apm_event) {
|
||||||
OPMEV_DEBUGMESSAGE(PMEV_STANDBYREQ);
|
OPMEV_DEBUGMESSAGE(PMEV_STANDBYREQ);
|
||||||
apm_suspend(PMST_STANDBY);
|
if (apm_op_inprog == 0) {
|
||||||
|
apm_op_inprog++;
|
||||||
|
if (apm_record_event(sc, apm_event)) {
|
||||||
|
apm_suspend(PMST_STANDBY);
|
||||||
|
}
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
OPMEV_DEBUGMESSAGE(PMEV_SUSPENDREQ);
|
OPMEV_DEBUGMESSAGE(PMEV_SUSPENDREQ);
|
||||||
apm_suspend(PMST_SUSPEND);
|
apm_lastreq_notify();
|
||||||
break;
|
if (apm_op_inprog == 0) {
|
||||||
|
apm_op_inprog++;
|
||||||
|
if (apm_record_event(sc, apm_event)) {
|
||||||
|
apm_suspend(PMST_SUSPEND);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return; /* XXX skip the rest */
|
||||||
OPMEV_DEBUGMESSAGE(PMEV_USERSUSPENDREQ);
|
OPMEV_DEBUGMESSAGE(PMEV_USERSUSPENDREQ);
|
||||||
apm_suspend(PMST_SUSPEND);
|
apm_lastreq_notify();
|
||||||
break;
|
if (apm_op_inprog == 0) {
|
||||||
|
apm_op_inprog++;
|
||||||
|
if (apm_record_event(sc, apm_event)) {
|
||||||
|
apm_suspend(PMST_SUSPEND);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return; /* XXX skip the rest */
|
||||||
OPMEV_DEBUGMESSAGE(PMEV_CRITSUSPEND);
|
OPMEV_DEBUGMESSAGE(PMEV_CRITSUSPEND);
|
||||||
apm_suspend(PMST_SUSPEND);
|
apm_suspend(PMST_SUSPEND);
|
||||||
break;
|
break;
|
||||||
OPMEV_DEBUGMESSAGE(PMEV_NORMRESUME);
|
OPMEV_DEBUGMESSAGE(PMEV_NORMRESUME);
|
||||||
|
apm_record_event(sc, apm_event);
|
||||||
apm_resume();
|
apm_resume();
|
||||||
break;
|
break;
|
||||||
OPMEV_DEBUGMESSAGE(PMEV_CRITRESUME);
|
OPMEV_DEBUGMESSAGE(PMEV_CRITRESUME);
|
||||||
|
apm_record_event(sc, apm_event);
|
||||||
apm_resume();
|
apm_resume();
|
||||||
break;
|
break;
|
||||||
OPMEV_DEBUGMESSAGE(PMEV_STANDBYRESUME);
|
OPMEV_DEBUGMESSAGE(PMEV_STANDBYRESUME);
|
||||||
|
apm_record_event(sc, apm_event);
|
||||||
apm_resume();
|
apm_resume();
|
||||||
break;
|
break;
|
||||||
OPMEV_DEBUGMESSAGE(PMEV_BATTERYLOW);
|
OPMEV_DEBUGMESSAGE(PMEV_BATTERYLOW);
|
||||||
apm_battery_low();
|
if (apm_record_event(sc, apm_event)) {
|
||||||
apm_suspend(PMST_SUSPEND);
|
apm_battery_low();
|
||||||
|
apm_suspend(PMST_SUSPEND);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
OPMEV_DEBUGMESSAGE(PMEV_POWERSTATECHANGE);
|
OPMEV_DEBUGMESSAGE(PMEV_POWERSTATECHANGE);
|
||||||
|
apm_record_event(sc, apm_event);
|
||||||
break;
|
break;
|
||||||
OPMEV_DEBUGMESSAGE(PMEV_UPDATETIME);
|
OPMEV_DEBUGMESSAGE(PMEV_UPDATETIME);
|
||||||
|
apm_record_event(sc, apm_event);
|
||||||
inittodr(0); /* adjust time to RTC */
|
inittodr(0); /* adjust time to RTC */
|
||||||
break;
|
break;
|
||||||
case PMEV_NOEVENT:
|
case PMEV_NOEVENT:
|
||||||
@ -970,16 +1093,50 @@ static int
|
|||||||
apmopen(dev_t dev, int flag, int fmt, struct proc *p)
|
apmopen(dev_t dev, int flag, int fmt, struct proc *p)
|
||||||
{
|
{
|
||||||
struct apm_softc *sc = &apm_softc;
|
struct apm_softc *sc = &apm_softc;
|
||||||
|
int ctl = APMDEV(dev);
|
||||||
|
|
||||||
if (minor(dev) != 0 || !sc->initialized)
|
if (!sc->initialized)
|
||||||
return (ENXIO);
|
return (ENXIO);
|
||||||
|
|
||||||
|
switch (ctl) {
|
||||||
|
case APMDEV_CTL:
|
||||||
|
if (!(flag & FWRITE))
|
||||||
|
return EINVAL;
|
||||||
|
if (sc->sc_flags & SCFLAG_OCTL)
|
||||||
|
return EBUSY;
|
||||||
|
sc->sc_flags |= SCFLAG_OCTL;
|
||||||
|
bzero(sc->event_filter, sizeof sc->event_filter);
|
||||||
|
break;
|
||||||
|
case APMDEV_NORMAL:
|
||||||
|
sc->sc_flags |= SCFLAG_ONORMAL;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return ENXIO;
|
||||||
|
break;
|
||||||
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
apmclose(dev_t dev, int flag, int fmt, struct proc *p)
|
apmclose(dev_t dev, int flag, int fmt, struct proc *p)
|
||||||
{
|
{
|
||||||
|
struct apm_softc *sc = &apm_softc;
|
||||||
|
int ctl = APMDEV(dev);
|
||||||
|
|
||||||
|
switch (ctl) {
|
||||||
|
case APMDEV_CTL:
|
||||||
|
apm_lastreq_rejected();
|
||||||
|
sc->sc_flags &= ~SCFLAG_OCTL;
|
||||||
|
bzero(sc->event_filter, sizeof sc->event_filter);
|
||||||
|
break;
|
||||||
|
case APMDEV_NORMAL:
|
||||||
|
sc->sc_flags &= ~SCFLAG_ONORMAL;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if ((sc->sc_flags & SCFLAG_OPEN) == 0) {
|
||||||
|
sc->event_count = 0;
|
||||||
|
sc->event_ptr = 0;
|
||||||
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -990,7 +1147,7 @@ apmioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, struct proc *p)
|
|||||||
int error = 0;
|
int error = 0;
|
||||||
int newstate;
|
int newstate;
|
||||||
|
|
||||||
if (minor(dev) != 0 || !sc->initialized)
|
if (!sc->initialized)
|
||||||
return (ENXIO);
|
return (ENXIO);
|
||||||
#ifdef APM_DEBUG
|
#ifdef APM_DEBUG
|
||||||
printf("APM ioctl: cmd = 0x%x\n", cmd);
|
printf("APM ioctl: cmd = 0x%x\n", cmd);
|
||||||
@ -1055,9 +1212,86 @@ apmioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, struct proc *p)
|
|||||||
error = EINVAL;
|
error = EINVAL;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* for /dev/apmctl */
|
||||||
|
if (APMDEV(dev) == APMDEV_CTL) {
|
||||||
|
struct apm_event_info *evp;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
error = 0;
|
||||||
|
switch (cmd) {
|
||||||
|
case APMIO_NEXTEVENT:
|
||||||
|
if (!sc->event_count) {
|
||||||
|
error = EAGAIN;
|
||||||
|
} else {
|
||||||
|
evp = (struct apm_event_info *)addr;
|
||||||
|
i = sc->event_ptr + APM_NEVENTS - sc->event_count;
|
||||||
|
i %= APM_NEVENTS;
|
||||||
|
*evp = sc->event_list[i];
|
||||||
|
sc->event_count--;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case APMIO_REJECTLASTREQ:
|
||||||
|
if (apm_lastreq_rejected()) {
|
||||||
|
error = EINVAL;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
error = EINVAL;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
apmwrite(dev_t dev, struct uio *uio, int ioflag)
|
||||||
|
{
|
||||||
|
struct apm_softc *sc = &apm_softc;
|
||||||
|
u_int event_type;
|
||||||
|
int error;
|
||||||
|
u_char enabled;
|
||||||
|
|
||||||
|
if (APMDEV(dev) != APMDEV_CTL)
|
||||||
|
return(ENODEV);
|
||||||
|
if (uio->uio_resid != sizeof(u_int))
|
||||||
|
return(E2BIG);
|
||||||
|
|
||||||
|
if ((error = uiomove((caddr_t)&event_type, sizeof(u_int), uio)))
|
||||||
|
return(error);
|
||||||
|
|
||||||
|
if (event_type < 0 || event_type >= APM_NPMEV)
|
||||||
|
return(EINVAL);
|
||||||
|
|
||||||
|
if (sc->event_filter[event_type] == 0) {
|
||||||
|
enabled = 1;
|
||||||
|
} else {
|
||||||
|
enabled = 0;
|
||||||
|
}
|
||||||
|
sc->event_filter[event_type] = enabled;
|
||||||
|
#ifdef APM_DEBUG
|
||||||
|
printf("apmwrite: event 0x%x %s\n", event_type, is_enabled(enabled));
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return uio->uio_resid;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
apmpoll(dev_t dev, int events, struct proc *p)
|
||||||
|
{
|
||||||
|
struct apm_softc *sc = &apm_softc;
|
||||||
|
int revents = 0;
|
||||||
|
|
||||||
|
if (events & (POLLIN | POLLRDNORM))
|
||||||
|
if (sc->event_count)
|
||||||
|
revents |= events & (POLLIN | POLLRDNORM);
|
||||||
|
else
|
||||||
|
selrecord(p, &sc->sc_rsel);
|
||||||
|
|
||||||
|
return (revents);
|
||||||
|
}
|
||||||
|
|
||||||
static device_method_t apm_methods[] = {
|
static device_method_t apm_methods[] = {
|
||||||
/* Device interface */
|
/* Device interface */
|
||||||
DEVMETHOD(device_probe, apm_probe),
|
DEVMETHOD(device_probe, apm_probe),
|
||||||
|
@ -12,7 +12,7 @@
|
|||||||
*
|
*
|
||||||
* Aug, 1994 Implemented on FreeBSD 1.1.5.1R (Toshiba AVS001WD)
|
* Aug, 1994 Implemented on FreeBSD 1.1.5.1R (Toshiba AVS001WD)
|
||||||
*
|
*
|
||||||
* $Id: apm_bios.h,v 1.20 1998/07/06 06:29:05 imp Exp $
|
* $Id: apm_bios.h,v 1.21 1998/10/30 05:41:15 msmith Exp $
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef _MACHINE_APM_BIOS_H_
|
#ifndef _MACHINE_APM_BIOS_H_
|
||||||
@ -233,6 +233,12 @@ struct apm_bios_arg {
|
|||||||
u_long edi;
|
u_long edi;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct apm_event_info {
|
||||||
|
u_int type;
|
||||||
|
u_int index;
|
||||||
|
u_int spare[8];
|
||||||
|
};
|
||||||
|
|
||||||
#define APMIO_SUSPEND _IO('P', 1)
|
#define APMIO_SUSPEND _IO('P', 1)
|
||||||
#define APMIO_GETINFO_OLD _IOR('P', 2, struct apm_info_old)
|
#define APMIO_GETINFO_OLD _IOR('P', 2, struct apm_info_old)
|
||||||
#define APMIO_ENABLE _IO('P', 5)
|
#define APMIO_ENABLE _IO('P', 5)
|
||||||
@ -243,6 +249,9 @@ struct apm_bios_arg {
|
|||||||
#define APMIO_BIOS _IOWR('P', 10, struct apm_bios_arg)
|
#define APMIO_BIOS _IOWR('P', 10, struct apm_bios_arg)
|
||||||
#define APMIO_GETINFO _IOR('P', 11, struct apm_info)
|
#define APMIO_GETINFO _IOR('P', 11, struct apm_info)
|
||||||
#define APMIO_STANDBY _IO('P', 12)
|
#define APMIO_STANDBY _IO('P', 12)
|
||||||
|
/* for /dev/apmctl */
|
||||||
|
#define APMIO_NEXTEVENT _IOR('A', 100, struct apm_event_info)
|
||||||
|
#define APMIO_REJECTLASTREQ _IO('P', 101)
|
||||||
|
|
||||||
#endif /* !ASSEMBLER && !INITIALIZER */
|
#endif /* !ASSEMBLER && !INITIALIZER */
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user