Changeset View
Changeset View
Standalone View
Standalone View
head/sys/i386/bios/apm.c
Show All 10 Lines | |||||
* these terms are retained. Under no circumstances is the author | * these terms are retained. Under no circumstances is the author | ||||
* responsible for the proper functioning of this software, nor does | * responsible for the proper functioning of this software, nor does | ||||
* the author assume any responsibility for damages incurred with its | * the author assume any responsibility for damages incurred with its | ||||
* use. | * use. | ||||
* | * | ||||
* Sep, 1994 Implemented on FreeBSD 1.1.5.1R (Toshiba AVS001WD) | * Sep, 1994 Implemented on FreeBSD 1.1.5.1R (Toshiba AVS001WD) | ||||
*/ | */ | ||||
/*- | |||||
* Copyright (c) 2000 Mitsuru IWASAKI <iwasaki@FreeBSD.org> | |||||
* All rights reserved. | |||||
* | |||||
* Redistribution and use in source and binary forms, with or without | |||||
* modification, are permitted provided that the following conditions | |||||
* are met: | |||||
* 1. Redistributions of source code must retain the above copyright | |||||
* notice, this list of conditions and the following disclaimer. | |||||
* 2. Redistributions in binary form must reproduce the above copyright | |||||
* notice, this list of conditions and the following disclaimer in the | |||||
* documentation and/or other materials provided with the distribution. | |||||
* | |||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND | |||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE | |||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |||||
* SUCH DAMAGE. | |||||
*/ | |||||
#include <sys/cdefs.h> | #include <sys/cdefs.h> | ||||
__FBSDID("$FreeBSD$"); | __FBSDID("$FreeBSD$"); | ||||
#include <sys/param.h> | #include <sys/param.h> | ||||
#include <sys/systm.h> | #include <sys/systm.h> | ||||
#include <sys/bus.h> | #include <sys/bus.h> | ||||
#include <sys/conf.h> | #include <sys/conf.h> | ||||
#include <sys/condvar.h> | #include <sys/condvar.h> | ||||
#include <sys/eventhandler.h> | #include <sys/eventhandler.h> | ||||
#include <sys/fcntl.h> | #include <sys/fcntl.h> | ||||
#include <sys/kernel.h> | #include <sys/kernel.h> | ||||
#include <sys/kthread.h> | #include <sys/kthread.h> | ||||
#include <sys/lock.h> | #include <sys/lock.h> | ||||
#include <sys/module.h> | #include <sys/module.h> | ||||
#include <sys/mutex.h> | #include <sys/mutex.h> | ||||
#include <sys/poll.h> | #include <sys/poll.h> | ||||
#include <sys/power.h> | #include <sys/power.h> | ||||
#include <sys/reboot.h> | #include <sys/reboot.h> | ||||
#include <sys/selinfo.h> | #include <sys/selinfo.h> | ||||
#include <sys/signalvar.h> | #include <sys/signalvar.h> | ||||
#include <sys/sysctl.h> | #include <sys/sysctl.h> | ||||
#include <sys/syslog.h> | #include <sys/syslog.h> | ||||
#include <sys/timetc.h> | |||||
#include <sys/time.h> | #include <sys/time.h> | ||||
#include <sys/uio.h> | #include <sys/uio.h> | ||||
#include <machine/apm_bios.h> | #include <machine/apm_bios.h> | ||||
#include <machine/clock.h> | #include <machine/clock.h> | ||||
#include <machine/endian.h> | #include <machine/endian.h> | ||||
#include <machine/pc/bios.h> | #include <machine/pc/bios.h> | ||||
#include <machine/cpufunc.h> | #include <machine/cpufunc.h> | ||||
▲ Show 20 Lines • Show All 151 Lines • ▼ Show 20 Lines | apm_enable_disable_pm(int enable) | ||||
return (apm_bioscall()); | return (apm_bioscall()); | ||||
} | } | ||||
/* register driver version (APM 1.1 or later) */ | /* register driver version (APM 1.1 or later) */ | ||||
static int | static int | ||||
apm_driver_version(int version) | apm_driver_version(int version) | ||||
{ | { | ||||
struct apm_softc *sc = &apm_softc; | struct apm_softc *sc = &apm_softc; | ||||
sc->bios.r.eax = (APM_BIOS << 8) | APM_DRVVERSION; | sc->bios.r.eax = (APM_BIOS << 8) | APM_DRVVERSION; | ||||
sc->bios.r.ebx = 0x0; | sc->bios.r.ebx = 0x0; | ||||
sc->bios.r.ecx = version; | sc->bios.r.ecx = version; | ||||
sc->bios.r.edx = 0; | sc->bios.r.edx = 0; | ||||
if (apm_bioscall() == 0 && sc->bios.r.eax == version) | if (apm_bioscall() == 0 && sc->bios.r.eax == version) | ||||
return (0); | return (0); | ||||
/* Some old BIOSes don't return the connection version in %ax. */ | /* Some old BIOSes don't return the connection version in %ax. */ | ||||
if (sc->bios.r.eax == ((APM_BIOS << 8) | APM_DRVVERSION)) | if (sc->bios.r.eax == ((APM_BIOS << 8) | APM_DRVVERSION)) | ||||
return (0); | return (0); | ||||
return (1); | return (1); | ||||
} | } | ||||
/* engage/disengage power management (APM 1.1 or later) */ | /* engage/disengage power management (APM 1.1 or later) */ | ||||
static int | static int | ||||
apm_engage_disengage_pm(int engage) | apm_engage_disengage_pm(int engage) | ||||
{ | { | ||||
struct apm_softc *sc = &apm_softc; | struct apm_softc *sc = &apm_softc; | ||||
sc->bios.r.eax = (APM_BIOS << 8) | APM_ENGAGEDISENGAGEPM; | sc->bios.r.eax = (APM_BIOS << 8) | APM_ENGAGEDISENGAGEPM; | ||||
sc->bios.r.ebx = PMDV_ALLDEV; | sc->bios.r.ebx = PMDV_ALLDEV; | ||||
sc->bios.r.ecx = engage; | sc->bios.r.ecx = engage; | ||||
sc->bios.r.edx = 0; | sc->bios.r.edx = 0; | ||||
return (apm_bioscall()); | return (apm_bioscall()); | ||||
} | } | ||||
/* get PM event */ | /* get PM event */ | ||||
static u_int | static u_int | ||||
apm_getevent(void) | apm_getevent(void) | ||||
{ | { | ||||
struct apm_softc *sc = &apm_softc; | struct apm_softc *sc = &apm_softc; | ||||
sc->bios.r.eax = (APM_BIOS << 8) | APM_GETPMEVENT; | sc->bios.r.eax = (APM_BIOS << 8) | APM_GETPMEVENT; | ||||
sc->bios.r.ebx = 0; | sc->bios.r.ebx = 0; | ||||
sc->bios.r.ecx = 0; | sc->bios.r.ecx = 0; | ||||
sc->bios.r.edx = 0; | sc->bios.r.edx = 0; | ||||
if (apm_bioscall()) | if (apm_bioscall()) | ||||
return (PMEV_NOEVENT); | return (PMEV_NOEVENT); | ||||
return (sc->bios.r.ebx & 0xffff); | return (sc->bios.r.ebx & 0xffff); | ||||
} | } | ||||
/* suspend entire system */ | /* suspend entire system */ | ||||
static int | static int | ||||
apm_suspend_system(int state) | apm_suspend_system(int state) | ||||
{ | { | ||||
struct apm_softc *sc = &apm_softc; | struct apm_softc *sc = &apm_softc; | ||||
sc->bios.r.eax = (APM_BIOS << 8) | APM_SETPWSTATE; | sc->bios.r.eax = (APM_BIOS << 8) | APM_SETPWSTATE; | ||||
sc->bios.r.ebx = PMDV_ALLDEV; | sc->bios.r.ebx = PMDV_ALLDEV; | ||||
sc->bios.r.ecx = state; | sc->bios.r.ecx = state; | ||||
sc->bios.r.edx = 0; | sc->bios.r.edx = 0; | ||||
if (apm_bioscall()) { | if (apm_bioscall()) { | ||||
printf("Entire system suspend failure: errcode = %d\n", | printf("Entire system suspend failure: errcode = %d\n", | ||||
0xff & (sc->bios.r.eax >> 8)); | 0xff & (sc->bios.r.eax >> 8)); | ||||
return 1; | return 1; | ||||
} | } | ||||
return 0; | return 0; | ||||
} | } | ||||
/* Display control */ | /* Display control */ | ||||
/* | /* | ||||
* Experimental implementation: My laptop machine can't handle this function | * Experimental implementation: My laptop machine can't handle this function | ||||
* If your laptop can control the display via APM, please inform me. | * If your laptop can control the display via APM, please inform me. | ||||
* HOSOKAWA, Tatsumi <hosokawa@jp.FreeBSD.org> | * HOSOKAWA, Tatsumi <hosokawa@jp.FreeBSD.org> | ||||
*/ | */ | ||||
int | int | ||||
apm_display(int newstate) | apm_display(int newstate) | ||||
{ | { | ||||
struct apm_softc *sc = &apm_softc; | struct apm_softc *sc = &apm_softc; | ||||
sc->bios.r.eax = (APM_BIOS << 8) | APM_SETPWSTATE; | sc->bios.r.eax = (APM_BIOS << 8) | APM_SETPWSTATE; | ||||
sc->bios.r.ebx = PMDV_DISP0; | sc->bios.r.ebx = PMDV_DISP0; | ||||
sc->bios.r.ecx = newstate ? PMST_APMENABLED:PMST_SUSPEND; | sc->bios.r.ecx = newstate ? PMST_APMENABLED:PMST_SUSPEND; | ||||
sc->bios.r.edx = 0; | sc->bios.r.edx = 0; | ||||
if (apm_bioscall() == 0) { | if (apm_bioscall() == 0) { | ||||
return 0; | return 0; | ||||
} | } | ||||
Show All 34 Lines | |||||
{ | { | ||||
printf("\007\007 * * * BATTERY IS LOW * * * \007\007"); | printf("\007\007 * * * BATTERY IS LOW * * * \007\007"); | ||||
} | } | ||||
/* APM hook manager */ | /* APM hook manager */ | ||||
static struct apmhook * | static struct apmhook * | ||||
apm_add_hook(struct apmhook **list, struct apmhook *ah) | apm_add_hook(struct apmhook **list, struct apmhook *ah) | ||||
{ | { | ||||
int s; | |||||
struct apmhook *p, *prev; | struct apmhook *p, *prev; | ||||
APM_DPRINT("Add hook \"%s\"\n", ah->ah_name); | APM_DPRINT("Add hook \"%s\"\n", ah->ah_name); | ||||
s = splhigh(); | |||||
if (ah == NULL) | if (ah == NULL) | ||||
panic("illegal apm_hook!"); | panic("illegal apm_hook!"); | ||||
prev = NULL; | prev = NULL; | ||||
for (p = *list; p != NULL; prev = p, p = p->ah_next) | for (p = *list; p != NULL; prev = p, p = p->ah_next) | ||||
if (p->ah_order > ah->ah_order) | if (p->ah_order > ah->ah_order) | ||||
break; | break; | ||||
if (prev == NULL) { | if (prev == NULL) { | ||||
ah->ah_next = *list; | ah->ah_next = *list; | ||||
*list = ah; | *list = ah; | ||||
} else { | } else { | ||||
ah->ah_next = prev->ah_next; | ah->ah_next = prev->ah_next; | ||||
prev->ah_next = ah; | prev->ah_next = ah; | ||||
} | } | ||||
splx(s); | |||||
return ah; | return ah; | ||||
} | } | ||||
static void | static void | ||||
apm_del_hook(struct apmhook **list, struct apmhook *ah) | apm_del_hook(struct apmhook **list, struct apmhook *ah) | ||||
{ | { | ||||
int s; | |||||
struct apmhook *p, *prev; | struct apmhook *p, *prev; | ||||
s = splhigh(); | |||||
prev = NULL; | prev = NULL; | ||||
for (p = *list; p != NULL; prev = p, p = p->ah_next) | for (p = *list; p != NULL; prev = p, p = p->ah_next) | ||||
if (p == ah) | if (p == ah) | ||||
goto deleteit; | goto deleteit; | ||||
panic("Tried to delete unregistered apm_hook."); | panic("Tried to delete unregistered apm_hook."); | ||||
goto nosuchnode; | return; | ||||
deleteit: | deleteit: | ||||
if (prev != NULL) | if (prev != NULL) | ||||
prev->ah_next = p->ah_next; | prev->ah_next = p->ah_next; | ||||
else | else | ||||
*list = p->ah_next; | *list = p->ah_next; | ||||
nosuchnode: | |||||
splx(s); | |||||
} | } | ||||
/* APM driver calls some functions automatically */ | /* APM driver calls some functions automatically */ | ||||
static void | static void | ||||
apm_execute_hook(struct apmhook *list) | apm_execute_hook(struct apmhook *list) | ||||
{ | { | ||||
struct apmhook *p; | struct apmhook *p; | ||||
▲ Show 20 Lines • Show All 77 Lines • ▼ Show 20 Lines | apm_do_standby(void) | ||||
if (sc == NULL || sc->initialized == 0) | if (sc == NULL || sc->initialized == 0) | ||||
return; | return; | ||||
apm_op_inprog = 0; | apm_op_inprog = 0; | ||||
sc->standbys = sc->standby_countdown = 0; | sc->standbys = sc->standby_countdown = 0; | ||||
/* | /* | ||||
* As far as standby, we don't need to execute | * As far as standby, we don't need to execute | ||||
* all of suspend hooks. | * all of suspend hooks. | ||||
*/ | */ | ||||
if (apm_suspend_system(PMST_STANDBY) == 0) | if (apm_suspend_system(PMST_STANDBY) == 0) | ||||
apm_processevent(); | apm_processevent(); | ||||
return; | return; | ||||
} | } | ||||
static void | static void | ||||
▲ Show 20 Lines • Show All 562 Lines • ▼ Show 20 Lines | switch (apm_event) { | ||||
break; | break; | ||||
default: | default: | ||||
printf("Unknown Original APM Event 0x%x\n", apm_event); | printf("Unknown Original APM Event 0x%x\n", apm_event); | ||||
break; | break; | ||||
} | } | ||||
} while (apm_event != PMEV_NOEVENT); | } while (apm_event != PMEV_NOEVENT); | ||||
} | } | ||||
static struct timeval suspend_time; | |||||
static struct timeval diff_time; | |||||
static int | |||||
apm_rtc_suspend(void *arg __unused) | |||||
{ | |||||
microtime(&diff_time); | |||||
inittodr(0); | |||||
microtime(&suspend_time); | |||||
timevalsub(&diff_time, &suspend_time); | |||||
return (0); | |||||
} | |||||
static int | |||||
apm_rtc_resume(void *arg __unused) | |||||
{ | |||||
u_int second, minute, hour; | |||||
struct timeval resume_time, tmp_time; | |||||
struct timespec ts; | |||||
/* modified for adjkerntz */ | |||||
timer_restore(); /* restore the all timers */ | |||||
inittodr(0); /* adjust time to RTC */ | |||||
microtime(&resume_time); | |||||
getmicrotime(&tmp_time); | |||||
timevaladd(&tmp_time, &diff_time); | |||||
/* Calculate the delta time suspended */ | |||||
timevalsub(&resume_time, &suspend_time); | |||||
second = ts.tv_sec = resume_time.tv_sec; | |||||
ts.tv_nsec = 0; | |||||
tc_setclock(&ts); | |||||
#ifdef PMTIMER_FIXUP_CALLTODO | |||||
/* Fixup the calltodo list with the delta time. */ | |||||
adjust_timeout_calltodo(&resume_time); | |||||
#endif /* PMTIMER_FIXUP_CALLTODO */ | |||||
hour = second / 3600; | |||||
second %= 3600; | |||||
minute = second / 60; | |||||
second %= 60; | |||||
log(LOG_NOTICE, "wakeup from sleeping state (slept %02d:%02d:%02d)\n", | |||||
hour, minute, second); | |||||
return (0); | |||||
} | |||||
/* | /* | ||||
* Attach APM: | * Attach APM: | ||||
* | * | ||||
* Initialize APM driver | * Initialize APM driver | ||||
*/ | */ | ||||
static int | static int | ||||
apm_attach(device_t dev) | apm_attach(device_t dev) | ||||
▲ Show 20 Lines • Show All 65 Lines • ▼ Show 20 Lines | if (sc->intversion >= INTVERSION(1, 1) && sc->disengaged) { | ||||
if (apm_engage_disengage_pm(1)) { | if (apm_engage_disengage_pm(1)) { | ||||
APM_DPRINT("apm: *Warning* engage function failed err=[%x]", | APM_DPRINT("apm: *Warning* engage function failed err=[%x]", | ||||
(sc->bios.r.eax >> 8) & 0xff); | (sc->bios.r.eax >> 8) & 0xff); | ||||
APM_DPRINT(" (Docked or using external power?).\n"); | APM_DPRINT(" (Docked or using external power?).\n"); | ||||
} | } | ||||
} | } | ||||
/* Power the system off using APM */ | /* Power the system off using APM */ | ||||
EVENTHANDLER_REGISTER(shutdown_final, apm_power_off, NULL, | EVENTHANDLER_REGISTER(shutdown_final, apm_power_off, NULL, | ||||
SHUTDOWN_PRI_LAST); | SHUTDOWN_PRI_LAST); | ||||
/* Register APM again to pass the correct argument of pm_func. */ | /* Register APM again to pass the correct argument of pm_func. */ | ||||
power_pm_register(POWER_PM_TYPE_APM, apm_pm_func, sc); | power_pm_register(POWER_PM_TYPE_APM, apm_pm_func, sc); | ||||
sc->initialized = 1; | sc->initialized = 1; | ||||
sc->suspending = 0; | sc->suspending = 0; | ||||
sc->running = 0; | sc->running = 0; | ||||
make_dev(&apm_cdevsw, APMDEV_NORMAL, | make_dev(&apm_cdevsw, APMDEV_NORMAL, | ||||
UID_ROOT, GID_OPERATOR, 0664, "apm"); | UID_ROOT, GID_OPERATOR, 0664, "apm"); | ||||
make_dev(&apm_cdevsw, APMDEV_CTL, | make_dev(&apm_cdevsw, APMDEV_CTL, | ||||
UID_ROOT, GID_OPERATOR, 0660, "apmctl"); | UID_ROOT, GID_OPERATOR, 0660, "apmctl"); | ||||
sc->sc_suspend.ah_fun = apm_rtc_suspend; | |||||
sc->sc_suspend.ah_arg = sc; | |||||
apm_hook_establish(APM_HOOK_SUSPEND, &sc->sc_suspend); | |||||
sc->sc_resume.ah_fun = apm_rtc_resume; | |||||
sc->sc_resume.ah_arg = sc; | |||||
apm_hook_establish(APM_HOOK_RESUME, &sc->sc_resume); | |||||
return 0; | return 0; | ||||
} | } | ||||
static int | static int | ||||
apmopen(struct cdev *dev, int flag, int fmt, struct thread *td) | apmopen(struct cdev *dev, int flag, int fmt, struct thread *td) | ||||
{ | { | ||||
struct apm_softc *sc = &apm_softc; | struct apm_softc *sc = &apm_softc; | ||||
▲ Show 20 Lines • Show All 204 Lines • ▼ Show 20 Lines | apmwrite(struct cdev *dev, struct uio *uio, int ioflag) | ||||
if (dev2unit(dev) != APMDEV_CTL) | if (dev2unit(dev) != APMDEV_CTL) | ||||
return(ENODEV); | return(ENODEV); | ||||
if (uio->uio_resid != sizeof(u_int)) | if (uio->uio_resid != sizeof(u_int)) | ||||
return(E2BIG); | return(E2BIG); | ||||
if ((error = uiomove((caddr_t)&event_type, sizeof(u_int), uio))) | if ((error = uiomove((caddr_t)&event_type, sizeof(u_int), uio))) | ||||
return(error); | return(error); | ||||
if (event_type < 0 || event_type >= APM_NPMEV) | if (event_type >= APM_NPMEV) | ||||
return(EINVAL); | return(EINVAL); | ||||
if (sc->event_filter[event_type] == 0) { | if (sc->event_filter[event_type] == 0) { | ||||
enabled = 1; | enabled = 1; | ||||
} else { | } else { | ||||
enabled = 0; | enabled = 0; | ||||
} | } | ||||
sc->event_filter[event_type] = enabled; | sc->event_filter[event_type] = enabled; | ||||
▲ Show 20 Lines • Show All 90 Lines • Show Last 20 Lines |