Ripped out a lot of stuff made to circumvent the previous bogus assy stuff.

Ripped out the "hooks".  Until we know how we want to do it, we shouldn't
start hacking anyway.  Still panics my machine though.
This commit is contained in:
Poul-Henning Kamp 1994-10-02 17:40:38 +00:00
parent 797f2d22f0
commit 1ed171d405
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=3309
2 changed files with 278 additions and 646 deletions

View file

@ -1,3 +1,4 @@
#define APM_DEBUG 1
/*
* LP (Laptop Package)
*
@ -12,7 +13,7 @@
*
* Sep, 1994 Implemented on FreeBSD 1.1.5.1R (Toshiba AVS001WD)
*
* $Id: apm.c,v 1.3 1994/10/01 05:12:22 davidg Exp $
* $Id: apm.c,v 1.4 1994/10/02 01:45:41 phk Exp $
*/
#include "apm.h"
@ -33,6 +34,7 @@
#include "i386/isa/isa_device.h"
#include <machine/apm_bios.h>
#include <machine/segments.h>
#include <machine/clock.h>
#include <vm/vm.h>
#include <sys/syslog.h>
#include "apm_setup.h"
@ -46,6 +48,8 @@ static u_int cs_entry;
static u_int intversion;
static int idle_cpu, disabled, disengaged;
#define is_enabled(foo) ((foo) ? "enabled" : "disabled")
/* Map version number to integer (keeps ordering of version numbers) */
#define INTVERSION(major, minor) ((major)*100 + (minor))
@ -119,6 +123,37 @@ apm_enable_disable_pm(int enable)
return apm_int(&eax,&ebx,&ecx);
}
/* Tell APM-BIOS that WE will do 1.1 and see what they say... */
static void
apm_driver_version()
{
u_long eax,ebx,ecx,i;
#ifdef APM_DEBUG
eax = (APM_BIOS<<8) | APM_INSTCHECK;
ebx = 0x0;
ecx = 0x0101;
i = apm_int(&eax,&ebx,&ecx);
printf("[%04lx %04lx %04lx %ld %02x]\n",
eax,ebx,ecx,i,apm_errno);
#endif
eax = (APM_BIOS<<8) | APM_DRVVERSION;
ebx = 0x0;
ecx = 0x0101;
if(!apm_int(&eax,&ebx,&ecx))
apm_version = eax & 0xffff;
#ifdef APM_DEBUG
eax = (APM_BIOS<<8) | APM_INSTCHECK;
ebx = 0x0;
ecx = 0x0101;
i = apm_int(&eax,&ebx,&ecx);
printf("[%04lx %04lx %04lx %ld %02x]\n",
eax,ebx,ecx,i,apm_errno);
#endif
}
/* engage/disengage power management (APM 1.1 or later) */
static int
apm_engage_disengage_pm(int engage)
@ -142,18 +177,10 @@ apm_getevent(void)
if (apm_int(&eax,&ebx,&ecx))
return PMEV_NOEVENT;
return ebx & 0xffff;
}
/*
* In many cases, the first event that occured after resume, needs
* special treatment. This binary flag make this process possible.
* Initial value of this variable is 1, because the bootstrap
* condition is equivalent to resumed condition for the power
* manager.
*/
static int resumed_event = 1;
/* suspend entire system */
static int
apm_suspend_system(void)
@ -169,7 +196,6 @@ apm_suspend_system(void)
0xff & (eax >> 8));
return 1;
}
resumed_event = 1;
return 0;
}
@ -177,92 +203,11 @@ apm_suspend_system(void)
static void
apm_battery_low(void)
{
/* Currently, this routine has not been implemented. Sorry... */
}
/* APM driver calls some functions automatically when the system wakes up */
static void
apm_execute_hook(apm_hook_func_t list)
{
apm_hook_func_t p;
for (p = list; p != NULL; p = p->next) {
if ((*(p->func))()) {
printf("Warning: APM hook of %s failed", p->name);
}
}
}
/* APM hook manager */
static apm_hook_func_t
apm_hook_init(apm_hook_func_t *list, int (*func)(void), char *name, int order)
{
int pl;
apm_hook_func_t p, prev, new_node;
pl = splhigh();
new_node = malloc(sizeof(*new_node), M_DEVBUF, M_NOWAIT);
if (new_node == NULL) {
panic("Can't allocate device buffer for apm_resume_hook.");
}
new_node->func = func;
new_node->name = name;
#if 0
new_node->next = *list;
*list = new_node;
#else
prev = NULL;
for (p = *list; p != NULL; prev = p, p = p->next) {
if (p->order > order) {
break;
}
}
if (prev == NULL) {
new_node->next = *list;
*list = new_node;
}
else {
new_node->next = prev->next;
prev->next = new_node;
}
#endif
splx(pl);
return new_node;
}
void
apm_hook_delete(apm_hook_func_t *list, apm_hook_func_t delete_node)
{
int pl;
apm_hook_func_t p, prev;
pl = splhigh();
prev = NULL;
for (p = *list; p != NULL; prev = p, p = p->next) {
if (p == delete_node) {
goto deleteit;
}
}
panic("Tried to delete unregistered apm_resume_hook.");
goto nosuchnode;
deleteit:
if (prev != NULL) {
prev->next = p->next;
}
else {
*list = p->next;
}
free(delete_node, M_DEVBUF);
nosuchnode:
splx(pl);
printf("\007\007 * * * BATTERY IS LOW * * * \007\007");
}
static struct timeval suspend_time;
/* default APM hook functions */
static int
apm_default_resume(void)
{
@ -276,69 +221,23 @@ apm_default_resume(void)
second %= 3600;
minute = second / 60;
second %= 60;
log(LOG_NOTICE, "resumed from suspended mode (slept %02d:%02d:%02d)\n", hour, minute, second);
log(LOG_NOTICE, "resumed from suspended mode (slept %02d:%02d:%02d)\n",
hour, minute, second);
return 0;
}
static int
apm_default_suspend(void)
{
#if 0
int pl;
pl = splhigh();
sync(curproc, NULL, NULL);
splx(pl);
#endif
microtime(&suspend_time);
apm_suspend_system();
return 0;
}
/* list structure for hook */
static apm_hook_func_t apm_resume_hook = NULL;
static apm_hook_func_t apm_suspend_hook = NULL;
/* execute resume hook */
static void
apm_execute_resume_hook(void)
{
apm_execute_hook(apm_resume_hook);
}
/* add a node on resume hook */
apm_hook_func_t
apm_resume_hook_init(int (*func)(void), char *name, int order)
{
return apm_hook_init(&apm_resume_hook, func, name, order);
}
/* delete a node from resume hook */
void
apm_resume_hook_delete(apm_hook_func_t delete_node)
{
apm_hook_delete(&apm_resume_hook, delete_node);
}
/* execute suspend hook */
static void
apm_execute_suspend_hook(void)
{
apm_execute_hook(apm_suspend_hook);
}
/* add a node on resume hook */
apm_hook_func_t
apm_suspend_hook_init(int (*func)(void), char *name, int order)
{
return apm_hook_init(&apm_suspend_hook, func, name, order);
}
/* delete a node from resume hook */
void
apm_suspend_hook_delete(apm_hook_func_t delete_node)
{
apm_hook_delete(&apm_suspend_hook, delete_node);
}
/* get APM information */
static int
apm_get_info(apm_info_t aip)
@ -360,50 +259,8 @@ apm_get_info(apm_info_t aip)
}
/* Define equivalent event sets */
static int equiv_event_num = 0;
static struct apm_eqv_event equiv_events[APM_MAX_EQUIV_EVENTS];
static int
apm_def_eqv(apm_eqv_event_t aee)
{
if (equiv_event_num == APM_MAX_EQUIV_EVENTS) {
return 1;
}
memcpy(&equiv_events[equiv_event_num], aee, sizeof(struct apm_eqv_event));
equiv_event_num++;
return 0;
}
static void
apm_flush_eqv(void)
{
equiv_event_num = 0;
}
static void apm_processevent(void);
/*
* Public interface to the suspend/resume:
*
* Execute suspend and resume hook before and after sleep, respectively.
*/
void
apm_suspend_resume(void)
{
#ifdef APM_DEBUG
printf("Called apm_suspend_resume();\n");
#endif
if (apm_initialized) {
apm_execute_suspend_hook();
apm_suspend_system();
apm_execute_resume_hook();
apm_processevent();
}
}
/* inform APM BIOS that CPU is idle */
void
apm_cpu_idle(void)
@ -441,19 +298,14 @@ apm_cpu_busy(void)
/*
* APM timeout routine:
*
* This routine is automatically called by timer two times within one
* seconed.
* This routine is automatically called by timer once per second.
*/
static void
apm_timeout(void *arg1)
{
#ifdef APM_DEBUG
printf("t");
#endif
apm_processevent();
timeout(apm_timeout, NULL, hz / 2); /* 2 Hz */
/* APM driver must polls APM event a time per second */
timeout(apm_timeout, NULL, hz ); /* 1 Hz */
}
/* enable APM BIOS */
@ -504,7 +356,6 @@ int apmattach(struct isa_device *);
struct isa_driver apmdriver = { apmprobe, apmattach, "apm" };
/*
* probe APM (dummy):
*
@ -513,6 +364,7 @@ struct isa_driver apmdriver = { apmprobe, apmattach, "apm" };
* Current version uses real mode, but on future version, we want
* to use V86 mode in APM initialization.
*/
int
apmprobe(struct isa_device *dvp)
{
@ -521,134 +373,107 @@ apmprobe(struct isa_device *dvp)
/* silent */
return 0;
case APMINI_NOT32BIT:
printf("apm%d: 32bit connection is not supported.\n", dvp->id_unit);
printf("apm%d: 32bit connection is not supported.\n",
dvp->id_unit);
return 0;
case APMINI_CONNECTERR:
printf("apm%d: 32-bit connection error.\n", dvp->id_unit);
return 0;
}
if ((apm_version & 0xff00) != 0x0100) return 0;
if ((apm_version & 0x00f0) >= 0x00a0) return 0;
if ((apm_version & 0x000f) >= 0x000a) return 0;
#ifdef APM_DEBUG
printf("sizeof 48bit %d\n",sizeof(struct addr48));
#endif
return -1;
}
static const char *
is_enabled(int enabled)
{
if (enabled) {
return "enabled";
}
return "disabled";
}
/* Process APM event */
static void
apm_processevent(void)
{
int i, apm_event;
int apm_event;
while (1) {
if ((apm_event = apm_getevent()) == PMEV_NOEVENT) {
break;
}
#if 1
#ifdef APM_DEBUG
# define OPMEV_DEBUGMESSAGE(symbol) case symbol: \
printf("Original APM Event: " #symbol "\n"); break
printf("Original APM Event: " #symbol "\n");
#else
# define OPMEV_DEBUGMESSAGE(symbol) case symbol: break;
# define OPMEV_DEBUGMESSAGE(symbol) case symbol:
#endif
switch (apm_event) {
OPMEV_DEBUGMESSAGE(PMEV_NOEVENT);
OPMEV_DEBUGMESSAGE(PMEV_STANDBYREQ);
OPMEV_DEBUGMESSAGE(PMEV_SUSPENDREQ);
OPMEV_DEBUGMESSAGE(PMEV_NORMRESUME);
OPMEV_DEBUGMESSAGE(PMEV_CRITRESUME);
OPMEV_DEBUGMESSAGE(PMEV_BATTERYLOW);
OPMEV_DEBUGMESSAGE(PMEV_POWERSTATECHANGE);
OPMEV_DEBUGMESSAGE(PMEV_UPDATETIME);
OPMEV_DEBUGMESSAGE(PMEV_CRITSUSPEND);
OPMEV_DEBUGMESSAGE(PMEV_USERSUSPENDREQ);
OPMEV_DEBUGMESSAGE(PMEV_STANDBYRESUME);
default:
printf("Unknown Original APM Event 0x%x\n", apm_event);
break;
}
#endif
for (i = 0; i < equiv_event_num; i++) {
if (equiv_events[i].aee_event == apm_event) {
u_int tmp = PMEV_DEFAULT;
if (resumed_event) {
tmp = equiv_events[i].aee_resume;
}
else {
tmp = equiv_events[i].aee_equiv;
}
if (tmp != PMEV_DEFAULT) {
apm_event = tmp;
break;
}
}
}
#if 1
#ifdef APM_DEBUG
# define PMEV_DEBUGMESSAGE(symbol) case symbol: \
printf("APM Event: " #symbol "\n"); break
#else
# define PMEV_DEBUGMESSAGE(symbol) case symbol: break;
#endif
switch (apm_event) {
PMEV_DEBUGMESSAGE(PMEV_NOEVENT);
PMEV_DEBUGMESSAGE(PMEV_STANDBYREQ);
PMEV_DEBUGMESSAGE(PMEV_SUSPENDREQ);
PMEV_DEBUGMESSAGE(PMEV_NORMRESUME);
PMEV_DEBUGMESSAGE(PMEV_CRITRESUME);
PMEV_DEBUGMESSAGE(PMEV_BATTERYLOW);
PMEV_DEBUGMESSAGE(PMEV_POWERSTATECHANGE);
PMEV_DEBUGMESSAGE(PMEV_UPDATETIME);
PMEV_DEBUGMESSAGE(PMEV_CRITSUSPEND);
PMEV_DEBUGMESSAGE(PMEV_USERSUSPENDREQ);
PMEV_DEBUGMESSAGE(PMEV_STANDBYRESUME);
default:
printf("Unknown APM Event 0x%x\n", apm_event);
break;
}
#endif
switch (apm_event) {
case PMEV_NOEVENT:
case PMEV_STANDBYREQ:
case PMEV_POWERSTATECHANGE:
case PMEV_CRITSUSPEND:
case PMEV_USERSTANDBYREQ:
case PMEV_USERSUSPENDREQ:
while (1) {
apm_event = apm_getevent();
if (apm_event == PMEV_NOEVENT)
break;
case PMEV_BATTERYLOW:
switch (apm_event) {
OPMEV_DEBUGMESSAGE(PMEV_STANDBYREQ);
apm_default_suspend();
break;
OPMEV_DEBUGMESSAGE(PMEV_SUSPENDREQ);
apm_default_suspend();
break;
OPMEV_DEBUGMESSAGE(PMEV_USERSUSPENDREQ);
apm_default_suspend();
break;
OPMEV_DEBUGMESSAGE(PMEV_CRITSUSPEND);
apm_default_suspend();
break;
OPMEV_DEBUGMESSAGE(PMEV_NORMRESUME);
apm_default_resume();
break;
OPMEV_DEBUGMESSAGE(PMEV_CRITRESUME);
apm_default_resume();
break;
OPMEV_DEBUGMESSAGE(PMEV_STANDBYRESUME);
apm_default_resume();
break;
OPMEV_DEBUGMESSAGE(PMEV_BATTERYLOW);
apm_battery_low();
apm_default_suspend();
break;
case PMEV_SUSPENDREQ:
apm_suspend_resume();
OPMEV_DEBUGMESSAGE(PMEV_POWERSTATECHANGE);
break;
case PMEV_NORMRESUME:
case PMEV_CRITRESUME:
case PMEV_UPDATETIME:
case PMEV_STANDBYRESUME:
OPMEV_DEBUGMESSAGE(PMEV_UPDATETIME);
inittodr(0); /* adjust time to RTC */
break;
default:
printf("Unknown Original APM Event 0x%x\n", apm_event);
break;
}
}
resumed_event = 0;
}
/*
* Attach APM:
*
* Initialize APM driver (APM BIOS itself has been initialized in locore.s)
*
* Now, unless I'm mad, (not quite ruled out yet), the APM-1.1 spec is bogus:
*
* Appendix C says under the header "APM 1.0/APM 1.1 Modal BIOS Behavior"
* that "When an APM Driver connects with an APM 1.1 BIOS, the APM 1.1 BIOS
* will default to an APM 1.0 connection. After an APM Driver calls the APM
* Driver Version function, specifying that it supports APM 1.1, and [sic!]
* APM BIOS will change its behavior to an APM 1.1 connection. If the APM
* BIOS is an APM 1.0 BIOS, the APM Driver Version function call will fail,
* and the connection will remain an APM 1.0 connection."
*
* OK so I can establish a 1.0 connection, and then tell that I'm a 1.1
* and maybe then the BIOS will tell that it too is a 1.1.
* Fine.
* Now how will I ever get the segment-limits for instance ? There is no
* way I can see that I can get a 1.1 response back from an "APM Protected
* Mode 32-bit Interface Connect" function ???
*
* Who made this, Intel and Microsoft ? -- How did you guess !
*
* /phk
*/
int
@ -656,47 +481,32 @@ apmattach(struct isa_device *dvp)
{
/* setup APM parameters */
minorversion = ((apm_version & 0x00f0) >> 4) * 10 + ((apm_version & 0x000f) >> 0);
majorversion = ((apm_version & 0xf000) >> 12) * 10 + ((apm_version & 0x0f00) >> 8);
intversion = INTVERSION(majorversion, minorversion);
cs32_base = (apm_cs32_base << 4) + KERNBASE;
cs16_base = (apm_cs16_base << 4) + KERNBASE;
ds_base = (apm_ds_base << 4) + KERNBASE;
cs_limit = apm_cs_limit;
ds_limit = apm_ds_limit;
cs_entry = apm_cs_entry;
idle_cpu = ((apm_flags & APM_CPUIDLE_SLOW) != 0);
disabled = ((apm_flags & APM_DISABLED) != 0);
disengaged = ((apm_flags & APM_DISENGAGED) != 0);
/* print bootstrap messages */
#ifdef APM_DEBUG
printf(" found APM BIOS version %d.%d\n", majorversion, minorversion);
printf(" found APM BIOS version %04x\n", apm_version);
printf("apm%d: Code32 0x%08x, Code16 0x%08x, Data 0x%08x\n",
dvp->id_unit, cs32_base, cs16_base, ds_base);
printf("apm%d: Code entry 0x%08x, Idling CPU %s, Management %s\n",
dvp->id_unit, cs_entry, is_enabled(idle_cpu),
is_enabled(!disabled));
#else /* APM_DEBUG */
printf(" found APM BIOS version %d.%d\n", majorversion, minorversion);
printf("apm%d: Idling CPU %s\n", dvp->id_unit, is_enabled(idle_cpu));
printf("apm%d: CS_limit=%x, DS_limit=%x\n",
dvp->id_unit, cs_limit,ds_limit);
#endif /* APM_DEBUG */
/*
* APM 1.0 does not have:
*
* 1. segment limit parameters
*
* 2. engage/disengage operations
*/
if (intversion >= INTVERSION(1, 1)) {
printf("apm%d: Engaged control %s\n",
dvp->id_unit, is_enabled(!disengaged));
}
else {
cs_limit = 0xffff;
ds_limit = 0xffff;
}
cs_limit = 0xffff;
ds_limit = 0xffff;
/* setup GDT */
setup_apm_gdt(cs32_base, cs16_base, ds_base, cs_limit, ds_limit);
@ -705,6 +515,24 @@ apmattach(struct isa_device *dvp)
apm_addr.segment = GSEL(GAPMCODE32_SEL, SEL_KPL);
apm_addr.offset = cs_entry;
/* Try to kick bios into 1.1 mode */
apm_driver_version();
minorversion = ((apm_version & 0x00f0) >> 4) * 10 +
((apm_version & 0x000f) >> 0);
majorversion = ((apm_version & 0xf000) >> 12) * 10 +
((apm_version & 0x0f00) >> 8);
intversion = INTVERSION(majorversion, minorversion);
if (intversion >= INTVERSION(1, 1)) {
printf("apm%d: Engaged control %s\n",
dvp->id_unit, is_enabled(!disengaged));
}
printf(" found APM BIOS version %d.%d\n", majorversion, minorversion);
printf("apm%d: Idling CPU %s\n", dvp->id_unit, is_enabled(idle_cpu));
/* enable power management */
if (disabled) {
if (apm_enable_disable_pm(1)) {
@ -721,10 +549,10 @@ apmattach(struct isa_device *dvp)
}
}
apm_suspend_hook_init(apm_default_suspend, "default suspend", APM_MAX_ORDER);
apm_resume_hook_init (apm_default_resume , "default resume" , APM_MIN_ORDER);
apm_initialized = 1;
apm_event_enable();
return 0;
}
@ -734,12 +562,8 @@ apmopen(dev_t dev, int flag, int fmt, struct proc *p)
if (!apm_initialized) {
return ENXIO;
}
switch (minor(dev)) {
case 0: /* apm0 */
break;
defaults:
if (minor(dev))
return (ENXIO);
}
return 0;
}
@ -768,21 +592,13 @@ apmioctl(dev_t dev, int cmd, caddr_t addr, int flag, struct proc *p)
}
switch (cmd) {
case APMIO_SUSPEND:
apm_suspend_resume();
apm_default_suspend();
break;
case APMIO_GETINFO:
if (apm_get_info((apm_info_t)addr)) {
error = ENXIO;
}
break;
case APMIO_DEFEQV:
if (apm_def_eqv((apm_eqv_event_t)addr)) {
error = ENOSPC;
}
break;
case APMIO_FLUSHEQV:
apm_flush_eqv();
break;
case APMIO_ENABLE:
apm_event_enable();
break;

View file

@ -1,3 +1,4 @@
#define APM_DEBUG 1
/*
* LP (Laptop Package)
*
@ -12,7 +13,7 @@
*
* Sep, 1994 Implemented on FreeBSD 1.1.5.1R (Toshiba AVS001WD)
*
* $Id: apm.c,v 1.3 1994/10/01 05:12:22 davidg Exp $
* $Id: apm.c,v 1.4 1994/10/02 01:45:41 phk Exp $
*/
#include "apm.h"
@ -33,6 +34,7 @@
#include "i386/isa/isa_device.h"
#include <machine/apm_bios.h>
#include <machine/segments.h>
#include <machine/clock.h>
#include <vm/vm.h>
#include <sys/syslog.h>
#include "apm_setup.h"
@ -46,6 +48,8 @@ static u_int cs_entry;
static u_int intversion;
static int idle_cpu, disabled, disengaged;
#define is_enabled(foo) ((foo) ? "enabled" : "disabled")
/* Map version number to integer (keeps ordering of version numbers) */
#define INTVERSION(major, minor) ((major)*100 + (minor))
@ -119,6 +123,37 @@ apm_enable_disable_pm(int enable)
return apm_int(&eax,&ebx,&ecx);
}
/* Tell APM-BIOS that WE will do 1.1 and see what they say... */
static void
apm_driver_version()
{
u_long eax,ebx,ecx,i;
#ifdef APM_DEBUG
eax = (APM_BIOS<<8) | APM_INSTCHECK;
ebx = 0x0;
ecx = 0x0101;
i = apm_int(&eax,&ebx,&ecx);
printf("[%04lx %04lx %04lx %ld %02x]\n",
eax,ebx,ecx,i,apm_errno);
#endif
eax = (APM_BIOS<<8) | APM_DRVVERSION;
ebx = 0x0;
ecx = 0x0101;
if(!apm_int(&eax,&ebx,&ecx))
apm_version = eax & 0xffff;
#ifdef APM_DEBUG
eax = (APM_BIOS<<8) | APM_INSTCHECK;
ebx = 0x0;
ecx = 0x0101;
i = apm_int(&eax,&ebx,&ecx);
printf("[%04lx %04lx %04lx %ld %02x]\n",
eax,ebx,ecx,i,apm_errno);
#endif
}
/* engage/disengage power management (APM 1.1 or later) */
static int
apm_engage_disengage_pm(int engage)
@ -142,18 +177,10 @@ apm_getevent(void)
if (apm_int(&eax,&ebx,&ecx))
return PMEV_NOEVENT;
return ebx & 0xffff;
}
/*
* In many cases, the first event that occured after resume, needs
* special treatment. This binary flag make this process possible.
* Initial value of this variable is 1, because the bootstrap
* condition is equivalent to resumed condition for the power
* manager.
*/
static int resumed_event = 1;
/* suspend entire system */
static int
apm_suspend_system(void)
@ -169,7 +196,6 @@ apm_suspend_system(void)
0xff & (eax >> 8));
return 1;
}
resumed_event = 1;
return 0;
}
@ -177,92 +203,11 @@ apm_suspend_system(void)
static void
apm_battery_low(void)
{
/* Currently, this routine has not been implemented. Sorry... */
}
/* APM driver calls some functions automatically when the system wakes up */
static void
apm_execute_hook(apm_hook_func_t list)
{
apm_hook_func_t p;
for (p = list; p != NULL; p = p->next) {
if ((*(p->func))()) {
printf("Warning: APM hook of %s failed", p->name);
}
}
}
/* APM hook manager */
static apm_hook_func_t
apm_hook_init(apm_hook_func_t *list, int (*func)(void), char *name, int order)
{
int pl;
apm_hook_func_t p, prev, new_node;
pl = splhigh();
new_node = malloc(sizeof(*new_node), M_DEVBUF, M_NOWAIT);
if (new_node == NULL) {
panic("Can't allocate device buffer for apm_resume_hook.");
}
new_node->func = func;
new_node->name = name;
#if 0
new_node->next = *list;
*list = new_node;
#else
prev = NULL;
for (p = *list; p != NULL; prev = p, p = p->next) {
if (p->order > order) {
break;
}
}
if (prev == NULL) {
new_node->next = *list;
*list = new_node;
}
else {
new_node->next = prev->next;
prev->next = new_node;
}
#endif
splx(pl);
return new_node;
}
void
apm_hook_delete(apm_hook_func_t *list, apm_hook_func_t delete_node)
{
int pl;
apm_hook_func_t p, prev;
pl = splhigh();
prev = NULL;
for (p = *list; p != NULL; prev = p, p = p->next) {
if (p == delete_node) {
goto deleteit;
}
}
panic("Tried to delete unregistered apm_resume_hook.");
goto nosuchnode;
deleteit:
if (prev != NULL) {
prev->next = p->next;
}
else {
*list = p->next;
}
free(delete_node, M_DEVBUF);
nosuchnode:
splx(pl);
printf("\007\007 * * * BATTERY IS LOW * * * \007\007");
}
static struct timeval suspend_time;
/* default APM hook functions */
static int
apm_default_resume(void)
{
@ -276,69 +221,23 @@ apm_default_resume(void)
second %= 3600;
minute = second / 60;
second %= 60;
log(LOG_NOTICE, "resumed from suspended mode (slept %02d:%02d:%02d)\n", hour, minute, second);
log(LOG_NOTICE, "resumed from suspended mode (slept %02d:%02d:%02d)\n",
hour, minute, second);
return 0;
}
static int
apm_default_suspend(void)
{
#if 0
int pl;
pl = splhigh();
sync(curproc, NULL, NULL);
splx(pl);
#endif
microtime(&suspend_time);
apm_suspend_system();
return 0;
}
/* list structure for hook */
static apm_hook_func_t apm_resume_hook = NULL;
static apm_hook_func_t apm_suspend_hook = NULL;
/* execute resume hook */
static void
apm_execute_resume_hook(void)
{
apm_execute_hook(apm_resume_hook);
}
/* add a node on resume hook */
apm_hook_func_t
apm_resume_hook_init(int (*func)(void), char *name, int order)
{
return apm_hook_init(&apm_resume_hook, func, name, order);
}
/* delete a node from resume hook */
void
apm_resume_hook_delete(apm_hook_func_t delete_node)
{
apm_hook_delete(&apm_resume_hook, delete_node);
}
/* execute suspend hook */
static void
apm_execute_suspend_hook(void)
{
apm_execute_hook(apm_suspend_hook);
}
/* add a node on resume hook */
apm_hook_func_t
apm_suspend_hook_init(int (*func)(void), char *name, int order)
{
return apm_hook_init(&apm_suspend_hook, func, name, order);
}
/* delete a node from resume hook */
void
apm_suspend_hook_delete(apm_hook_func_t delete_node)
{
apm_hook_delete(&apm_suspend_hook, delete_node);
}
/* get APM information */
static int
apm_get_info(apm_info_t aip)
@ -360,50 +259,8 @@ apm_get_info(apm_info_t aip)
}
/* Define equivalent event sets */
static int equiv_event_num = 0;
static struct apm_eqv_event equiv_events[APM_MAX_EQUIV_EVENTS];
static int
apm_def_eqv(apm_eqv_event_t aee)
{
if (equiv_event_num == APM_MAX_EQUIV_EVENTS) {
return 1;
}
memcpy(&equiv_events[equiv_event_num], aee, sizeof(struct apm_eqv_event));
equiv_event_num++;
return 0;
}
static void
apm_flush_eqv(void)
{
equiv_event_num = 0;
}
static void apm_processevent(void);
/*
* Public interface to the suspend/resume:
*
* Execute suspend and resume hook before and after sleep, respectively.
*/
void
apm_suspend_resume(void)
{
#ifdef APM_DEBUG
printf("Called apm_suspend_resume();\n");
#endif
if (apm_initialized) {
apm_execute_suspend_hook();
apm_suspend_system();
apm_execute_resume_hook();
apm_processevent();
}
}
/* inform APM BIOS that CPU is idle */
void
apm_cpu_idle(void)
@ -441,19 +298,14 @@ apm_cpu_busy(void)
/*
* APM timeout routine:
*
* This routine is automatically called by timer two times within one
* seconed.
* This routine is automatically called by timer once per second.
*/
static void
apm_timeout(void *arg1)
{
#ifdef APM_DEBUG
printf("t");
#endif
apm_processevent();
timeout(apm_timeout, NULL, hz / 2); /* 2 Hz */
/* APM driver must polls APM event a time per second */
timeout(apm_timeout, NULL, hz ); /* 1 Hz */
}
/* enable APM BIOS */
@ -504,7 +356,6 @@ int apmattach(struct isa_device *);
struct isa_driver apmdriver = { apmprobe, apmattach, "apm" };
/*
* probe APM (dummy):
*
@ -513,6 +364,7 @@ struct isa_driver apmdriver = { apmprobe, apmattach, "apm" };
* Current version uses real mode, but on future version, we want
* to use V86 mode in APM initialization.
*/
int
apmprobe(struct isa_device *dvp)
{
@ -521,134 +373,107 @@ apmprobe(struct isa_device *dvp)
/* silent */
return 0;
case APMINI_NOT32BIT:
printf("apm%d: 32bit connection is not supported.\n", dvp->id_unit);
printf("apm%d: 32bit connection is not supported.\n",
dvp->id_unit);
return 0;
case APMINI_CONNECTERR:
printf("apm%d: 32-bit connection error.\n", dvp->id_unit);
return 0;
}
if ((apm_version & 0xff00) != 0x0100) return 0;
if ((apm_version & 0x00f0) >= 0x00a0) return 0;
if ((apm_version & 0x000f) >= 0x000a) return 0;
#ifdef APM_DEBUG
printf("sizeof 48bit %d\n",sizeof(struct addr48));
#endif
return -1;
}
static const char *
is_enabled(int enabled)
{
if (enabled) {
return "enabled";
}
return "disabled";
}
/* Process APM event */
static void
apm_processevent(void)
{
int i, apm_event;
int apm_event;
while (1) {
if ((apm_event = apm_getevent()) == PMEV_NOEVENT) {
break;
}
#if 1
#ifdef APM_DEBUG
# define OPMEV_DEBUGMESSAGE(symbol) case symbol: \
printf("Original APM Event: " #symbol "\n"); break
printf("Original APM Event: " #symbol "\n");
#else
# define OPMEV_DEBUGMESSAGE(symbol) case symbol: break;
# define OPMEV_DEBUGMESSAGE(symbol) case symbol:
#endif
switch (apm_event) {
OPMEV_DEBUGMESSAGE(PMEV_NOEVENT);
OPMEV_DEBUGMESSAGE(PMEV_STANDBYREQ);
OPMEV_DEBUGMESSAGE(PMEV_SUSPENDREQ);
OPMEV_DEBUGMESSAGE(PMEV_NORMRESUME);
OPMEV_DEBUGMESSAGE(PMEV_CRITRESUME);
OPMEV_DEBUGMESSAGE(PMEV_BATTERYLOW);
OPMEV_DEBUGMESSAGE(PMEV_POWERSTATECHANGE);
OPMEV_DEBUGMESSAGE(PMEV_UPDATETIME);
OPMEV_DEBUGMESSAGE(PMEV_CRITSUSPEND);
OPMEV_DEBUGMESSAGE(PMEV_USERSUSPENDREQ);
OPMEV_DEBUGMESSAGE(PMEV_STANDBYRESUME);
default:
printf("Unknown Original APM Event 0x%x\n", apm_event);
break;
}
#endif
for (i = 0; i < equiv_event_num; i++) {
if (equiv_events[i].aee_event == apm_event) {
u_int tmp = PMEV_DEFAULT;
if (resumed_event) {
tmp = equiv_events[i].aee_resume;
}
else {
tmp = equiv_events[i].aee_equiv;
}
if (tmp != PMEV_DEFAULT) {
apm_event = tmp;
break;
}
}
}
#if 1
#ifdef APM_DEBUG
# define PMEV_DEBUGMESSAGE(symbol) case symbol: \
printf("APM Event: " #symbol "\n"); break
#else
# define PMEV_DEBUGMESSAGE(symbol) case symbol: break;
#endif
switch (apm_event) {
PMEV_DEBUGMESSAGE(PMEV_NOEVENT);
PMEV_DEBUGMESSAGE(PMEV_STANDBYREQ);
PMEV_DEBUGMESSAGE(PMEV_SUSPENDREQ);
PMEV_DEBUGMESSAGE(PMEV_NORMRESUME);
PMEV_DEBUGMESSAGE(PMEV_CRITRESUME);
PMEV_DEBUGMESSAGE(PMEV_BATTERYLOW);
PMEV_DEBUGMESSAGE(PMEV_POWERSTATECHANGE);
PMEV_DEBUGMESSAGE(PMEV_UPDATETIME);
PMEV_DEBUGMESSAGE(PMEV_CRITSUSPEND);
PMEV_DEBUGMESSAGE(PMEV_USERSUSPENDREQ);
PMEV_DEBUGMESSAGE(PMEV_STANDBYRESUME);
default:
printf("Unknown APM Event 0x%x\n", apm_event);
break;
}
#endif
switch (apm_event) {
case PMEV_NOEVENT:
case PMEV_STANDBYREQ:
case PMEV_POWERSTATECHANGE:
case PMEV_CRITSUSPEND:
case PMEV_USERSTANDBYREQ:
case PMEV_USERSUSPENDREQ:
while (1) {
apm_event = apm_getevent();
if (apm_event == PMEV_NOEVENT)
break;
case PMEV_BATTERYLOW:
switch (apm_event) {
OPMEV_DEBUGMESSAGE(PMEV_STANDBYREQ);
apm_default_suspend();
break;
OPMEV_DEBUGMESSAGE(PMEV_SUSPENDREQ);
apm_default_suspend();
break;
OPMEV_DEBUGMESSAGE(PMEV_USERSUSPENDREQ);
apm_default_suspend();
break;
OPMEV_DEBUGMESSAGE(PMEV_CRITSUSPEND);
apm_default_suspend();
break;
OPMEV_DEBUGMESSAGE(PMEV_NORMRESUME);
apm_default_resume();
break;
OPMEV_DEBUGMESSAGE(PMEV_CRITRESUME);
apm_default_resume();
break;
OPMEV_DEBUGMESSAGE(PMEV_STANDBYRESUME);
apm_default_resume();
break;
OPMEV_DEBUGMESSAGE(PMEV_BATTERYLOW);
apm_battery_low();
apm_default_suspend();
break;
case PMEV_SUSPENDREQ:
apm_suspend_resume();
OPMEV_DEBUGMESSAGE(PMEV_POWERSTATECHANGE);
break;
case PMEV_NORMRESUME:
case PMEV_CRITRESUME:
case PMEV_UPDATETIME:
case PMEV_STANDBYRESUME:
OPMEV_DEBUGMESSAGE(PMEV_UPDATETIME);
inittodr(0); /* adjust time to RTC */
break;
default:
printf("Unknown Original APM Event 0x%x\n", apm_event);
break;
}
}
resumed_event = 0;
}
/*
* Attach APM:
*
* Initialize APM driver (APM BIOS itself has been initialized in locore.s)
*
* Now, unless I'm mad, (not quite ruled out yet), the APM-1.1 spec is bogus:
*
* Appendix C says under the header "APM 1.0/APM 1.1 Modal BIOS Behavior"
* that "When an APM Driver connects with an APM 1.1 BIOS, the APM 1.1 BIOS
* will default to an APM 1.0 connection. After an APM Driver calls the APM
* Driver Version function, specifying that it supports APM 1.1, and [sic!]
* APM BIOS will change its behavior to an APM 1.1 connection. If the APM
* BIOS is an APM 1.0 BIOS, the APM Driver Version function call will fail,
* and the connection will remain an APM 1.0 connection."
*
* OK so I can establish a 1.0 connection, and then tell that I'm a 1.1
* and maybe then the BIOS will tell that it too is a 1.1.
* Fine.
* Now how will I ever get the segment-limits for instance ? There is no
* way I can see that I can get a 1.1 response back from an "APM Protected
* Mode 32-bit Interface Connect" function ???
*
* Who made this, Intel and Microsoft ? -- How did you guess !
*
* /phk
*/
int
@ -656,47 +481,32 @@ apmattach(struct isa_device *dvp)
{
/* setup APM parameters */
minorversion = ((apm_version & 0x00f0) >> 4) * 10 + ((apm_version & 0x000f) >> 0);
majorversion = ((apm_version & 0xf000) >> 12) * 10 + ((apm_version & 0x0f00) >> 8);
intversion = INTVERSION(majorversion, minorversion);
cs32_base = (apm_cs32_base << 4) + KERNBASE;
cs16_base = (apm_cs16_base << 4) + KERNBASE;
ds_base = (apm_ds_base << 4) + KERNBASE;
cs_limit = apm_cs_limit;
ds_limit = apm_ds_limit;
cs_entry = apm_cs_entry;
idle_cpu = ((apm_flags & APM_CPUIDLE_SLOW) != 0);
disabled = ((apm_flags & APM_DISABLED) != 0);
disengaged = ((apm_flags & APM_DISENGAGED) != 0);
/* print bootstrap messages */
#ifdef APM_DEBUG
printf(" found APM BIOS version %d.%d\n", majorversion, minorversion);
printf(" found APM BIOS version %04x\n", apm_version);
printf("apm%d: Code32 0x%08x, Code16 0x%08x, Data 0x%08x\n",
dvp->id_unit, cs32_base, cs16_base, ds_base);
printf("apm%d: Code entry 0x%08x, Idling CPU %s, Management %s\n",
dvp->id_unit, cs_entry, is_enabled(idle_cpu),
is_enabled(!disabled));
#else /* APM_DEBUG */
printf(" found APM BIOS version %d.%d\n", majorversion, minorversion);
printf("apm%d: Idling CPU %s\n", dvp->id_unit, is_enabled(idle_cpu));
printf("apm%d: CS_limit=%x, DS_limit=%x\n",
dvp->id_unit, cs_limit,ds_limit);
#endif /* APM_DEBUG */
/*
* APM 1.0 does not have:
*
* 1. segment limit parameters
*
* 2. engage/disengage operations
*/
if (intversion >= INTVERSION(1, 1)) {
printf("apm%d: Engaged control %s\n",
dvp->id_unit, is_enabled(!disengaged));
}
else {
cs_limit = 0xffff;
ds_limit = 0xffff;
}
cs_limit = 0xffff;
ds_limit = 0xffff;
/* setup GDT */
setup_apm_gdt(cs32_base, cs16_base, ds_base, cs_limit, ds_limit);
@ -705,6 +515,24 @@ apmattach(struct isa_device *dvp)
apm_addr.segment = GSEL(GAPMCODE32_SEL, SEL_KPL);
apm_addr.offset = cs_entry;
/* Try to kick bios into 1.1 mode */
apm_driver_version();
minorversion = ((apm_version & 0x00f0) >> 4) * 10 +
((apm_version & 0x000f) >> 0);
majorversion = ((apm_version & 0xf000) >> 12) * 10 +
((apm_version & 0x0f00) >> 8);
intversion = INTVERSION(majorversion, minorversion);
if (intversion >= INTVERSION(1, 1)) {
printf("apm%d: Engaged control %s\n",
dvp->id_unit, is_enabled(!disengaged));
}
printf(" found APM BIOS version %d.%d\n", majorversion, minorversion);
printf("apm%d: Idling CPU %s\n", dvp->id_unit, is_enabled(idle_cpu));
/* enable power management */
if (disabled) {
if (apm_enable_disable_pm(1)) {
@ -721,10 +549,10 @@ apmattach(struct isa_device *dvp)
}
}
apm_suspend_hook_init(apm_default_suspend, "default suspend", APM_MAX_ORDER);
apm_resume_hook_init (apm_default_resume , "default resume" , APM_MIN_ORDER);
apm_initialized = 1;
apm_event_enable();
return 0;
}
@ -734,12 +562,8 @@ apmopen(dev_t dev, int flag, int fmt, struct proc *p)
if (!apm_initialized) {
return ENXIO;
}
switch (minor(dev)) {
case 0: /* apm0 */
break;
defaults:
if (minor(dev))
return (ENXIO);
}
return 0;
}
@ -768,21 +592,13 @@ apmioctl(dev_t dev, int cmd, caddr_t addr, int flag, struct proc *p)
}
switch (cmd) {
case APMIO_SUSPEND:
apm_suspend_resume();
apm_default_suspend();
break;
case APMIO_GETINFO:
if (apm_get_info((apm_info_t)addr)) {
error = ENXIO;
}
break;
case APMIO_DEFEQV:
if (apm_def_eqv((apm_eqv_event_t)addr)) {
error = ENOSPC;
}
break;
case APMIO_FLUSHEQV:
apm_flush_eqv();
break;
case APMIO_ENABLE:
apm_event_enable();
break;