Date: Mon, 15 Feb 1999 01:43:35 +0900 From: Mitsuru IWASAKI <iwasaki@jp.FreeBSD.org> To: Valentin Shopov <valsho@yahoo.com> Cc: mobile@FreeBSD.ORG, Nate Williams <nate@mt.sri.com>, Warner Losh <imp@village.org> Subject: Re: apm & current Message-ID: <199902141646.BAA21353@tasogare.imasy.or.jp> In-Reply-To: Your message of "Sat, 13 Feb 1999 07:39:58 PST." <19990213153958.2977.rocketmail@send1e.yahoomail.com> References: <19990213153958.2977.rocketmail@send1e.yahoomail.com>
next in thread | previous in thread | raw e-mail | index | archive | help
Hello, I'm now working on APM code for PAO in Japan.
> Also kernel panics every time when I run apm, but zzz - suspend is
> working.
I already noticed that this problem happens on some old laptops which has
apm v1.1 or 1.0 (Sotec WinBookPro DX4/100, DEC HiNote Ultra II, etc.).
-current/-stable apm(8) try to call APM v1.2 BIOS functions
(e.g. APM_RESUMETIMER) without version checking.
Some of the old APM BIOSes, however, can make kernel panic when recieved
unkown APM BIOS functions.
In /sys/i386/apm/apm.c, apm_get_info() has the same problem
(APM_GETCAPABILITIES), therefore xbatt also will cause the same trouble.
So, now I'm going to commit version checking code just before
calling apm_bios_call() in /sys/i386/apm/apm.c into PAO3 CVS repository.
Adding to this, I noticed many other problems in apm code, such as
- Fixed segment description for APM. The limit granularity should be
specified in bytes, not pages.
- Try to limit the number of apm_bios_call() executing to only one
at the same time, watching busy state made by previous call and
waiting if necessary.
- Made apm_suspend() and apm_standby() be invoked by apm_timeout() in order to
obtain stablities.
- Added adjustment for segment size limits informed by APM BIOS. Following
patch for apm_init.S and make in /sys/i386/apm/apm_init/ to generate
apm_init.inc are required if VM86 isn't enabled in your kernel.
--- /usr/src/sys/i386/apm/apm_init/apm_init.S Sat Feb 22 18:29:52 1997
+++ apm_init/apm_init.S Sun Feb 14 20:47:04 1999
@@ -150,6 +150,8 @@
movb $(APM_PROT32CONNECT), %al
data32
movl $(PMDV_APMBIOS), %ebx
+ xorl %esi, %esi /* XXX clear %esi for cs length */
+ xorl %edi, %edi /* XXX clear %edi for ds length */
sti
int $(SYSTEM_BIOS)
cli
Followings are diffs for /sys/i386/apm/apm.c between PAO3
(not commited yet) and 3.0-RELEASE, please take a look this code.
--- /usr/src/sys/i386/apm/apm.c Mon Sep 28 12:41:12 1998
+++ apm.c Sun Feb 14 22:01:16 1999
@@ -29,6 +29,7 @@
#endif /*DEVFS*/
#include <sys/systm.h>
#include <sys/time.h>
+#include <sys/reboot.h>
#include <i386/isa/isa_device.h>
#include <machine/apm_bios.h>
#include <machine/segments.h>
@@ -48,11 +49,17 @@
static int apm_int __P((u_long *eax, u_long *ebx, u_long *ecx, u_long *edx));
static void apm_resume __P((void));
+#define APM_FORCE_APM10_FLAG 0x02
+#define APM_NO_CLOCK_ADJUST_FLAG 0x04
+#define APM_FORCE_64K_SEG_FLAG 0x08
+
+
/* static data */
struct apm_softc {
int initialized, active;
int always_halt_cpu, slow_idle_cpu;
int disabled, disengaged;
+ int suspending;
u_int minorversion, majorversion;
u_int cs32_base, cs16_base, ds_base;
u_int cs16_limit, cs32_limit, ds_limit;
@@ -92,16 +99,54 @@
setup_apm_gdt(u_int code32_base, u_int code16_base, u_int data_base, u_int code32_limit, u_int code16_limit, u_int data_limit)
{
/* setup 32bit code segment */
+ /* iwasaki (1999/1/24)
+ * type : S bit = on, code segment, readable, accessed
+ * dpl : kernel priority level
+ * def32: default 32 bit size
+ * gran : limit granularity = byte units
+ */
gdt_segs[GAPMCODE32_SEL].ssd_base = code32_base;
gdt_segs[GAPMCODE32_SEL].ssd_limit = code32_limit;
+ gdt_segs[GAPMCODE32_SEL].ssd_type = SDT_MEMERA;
+ gdt_segs[GAPMCODE32_SEL].ssd_dpl = SEL_KPL;
+ gdt_segs[GAPMCODE32_SEL].ssd_def32 = 1;
+ gdt_segs[GAPMCODE32_SEL].ssd_gran = 0;
/* setup 16bit code segment */
+ /* iwasaki (1999/1/24)
+ * type : S bit = on, code segment, readable, accessed
+ * dpl : kernel priority level
+ * def32: default 16 bit size
+ * gran : limit granularity = byte units
+ */
gdt_segs[GAPMCODE16_SEL].ssd_base = code16_base;
gdt_segs[GAPMCODE16_SEL].ssd_limit = code16_limit;
+ gdt_segs[GAPMCODE16_SEL].ssd_type = SDT_MEMERA;
+ gdt_segs[GAPMCODE16_SEL].ssd_dpl = SEL_KPL;
+ gdt_segs[GAPMCODE16_SEL].ssd_def32 = 0;
+ gdt_segs[GAPMCODE16_SEL].ssd_gran = 0;
/* setup data segment */
+ /* iwasaki (1999/1/24)
+ * type : S bit = on, data segment, writable, accessed
+ * dpl : kernel priority level
+ * def32: default 32 bit size
+ * gran : limit granularity = byte units
+ */
gdt_segs[GAPMDATA_SEL ].ssd_base = data_base;
gdt_segs[GAPMDATA_SEL ].ssd_limit = data_limit;
+ gdt_segs[GAPMDATA_SEL ].ssd_type = SDT_MEMRWA;
+ gdt_segs[GAPMDATA_SEL ].ssd_dpl = SEL_KPL;
+ gdt_segs[GAPMDATA_SEL ].ssd_def32 = 1;
+ gdt_segs[GAPMDATA_SEL ].ssd_gran = 0;
+ if (data_limit == 0) {
+ /* iwasaki (1999/1/24)
+ * type : S bit = on, data segment, accessed
+ * def32: default 16 bit size
+ */
+ gdt_segs[GAPMDATA_SEL ].ssd_type = SDT_MEMROA;
+ gdt_segs[GAPMDATA_SEL ].ssd_def32 = 0;
+ }
/* reflect these changes on physical GDT */
ssdtosd(gdt_segs + GAPMCODE32_SEL, &gdt[GAPMCODE32_SEL].sd);
@@ -116,6 +161,49 @@
} apm_addr;
static int apm_errno;
+static int apm_bios_busy = 0;
+
+static int
+apm_do_int(struct apm_bios_arg *apap)
+{
+ struct apm_softc *sc = &apm_softc;
+ int cf;
+ int n = 0;
+ u_long apm_func;
+
+ apm_func = apap->eax & 0x00ff;
+ switch (sc->intversion) {
+ case INTVERSION(1, 0):
+ if (apm_func != APM_DRVVERSION &&
+ apm_func > APM_GETPMEVENT) {
+#ifdef APM_DEBUG
+printf("apm_int: function 0x%lx is not supported by APM V1.0\n", apm_func);
+#endif
+ return ENOSYS;
+ }
+ break;
+ case INTVERSION(1, 1):
+ if (apm_func != APM_DRVVERSION &&
+ apm_func > APM_ENGAGEDISENGAGEPM &&
+ apm_func < APM_OEMFUNC) {
+#ifdef APM_DEBUG
+printf("apm_int: function 0x%lx is not supported by APM V1.1\n", apm_func);
+#endif
+ return ENOSYS;
+ }
+ break;
+ case INTVERSION(1, 2):
+ break;
+ }
+
+ while (apm_bios_busy && n++ < 50) {
+ DELAY(100000L);
+ }
+ ++apm_bios_busy;
+ cf = apm_bios_call(apap);
+ apm_bios_busy = 0;
+ return cf;
+}
static int
apm_int(u_long *eax, u_long *ebx, u_long *ecx, u_long *edx)
@@ -127,7 +215,9 @@
apa.ebx = *ebx;
apa.ecx = *ecx;
apa.edx = *edx;
- cf = apm_bios_call(&apa);
+ apa.esi = 0; /* clear register */
+ apa.edi = 0; /* clear register */
+ cf = apm_do_int(&apa);
*eax = apa.eax;
*ebx = apa.ebx;
*ecx = apa.ecx;
@@ -156,12 +246,12 @@
return apm_int(&eax, &ebx, &ecx, &edx);
}
+/* Tell APM-BIOS that WE will do 1.1 and see what they say... */
static void
apm_driver_version(int version)
{
u_long eax, ebx, ecx, edx;
- /* First try APM 1.2 */
eax = (APM_BIOS << 8) | APM_DRVVERSION;
ebx = 0x0;
ecx = version;
@@ -246,17 +336,24 @@
* Turn off the entire system.
*/
void
-apm_power_off(void)
+apm_power_off(int dummy, void *dummy_arg)
{
u_long eax, ebx, ecx, edx;
if (!apm_softc.active)
return;
+
+ /* wait 1sec before turning off the system power */
+ DELAY(1000000);
+
eax = (APM_BIOS << 8) | APM_SETPWSTATE;
ebx = PMDV_ALLDEV;
ecx = PMST_OFF;
edx = 0;
- apm_int(&eax, &ebx, &ecx, &edx);
+ if (apm_int(&eax, &ebx, &ecx, &edx)) {
+ printf("Power off failure: errcode = %ld\n",
+ 0xff & (eax >> 8));
+ }
}
/* APM Battery low handler */
@@ -358,6 +455,7 @@
static struct timeval suspend_time;
static struct timeval diff_time;
+static int apm_no_clock_adjust = 0;
static int
apm_default_resume(void *arg)
@@ -366,40 +464,45 @@
u_int second, minute, hour;
struct timeval resume_time, tmp_time;
- /* modified for adjkerntz */
- pl = splsoftclock();
- inittodr(0); /* adjust time to RTC */
- microtime(&resume_time);
- getmicrotime(&tmp_time);
- timevaladd(&tmp_time, &diff_time);
+ if (apm_no_clock_adjust) {
+ log(LOG_NOTICE, "resumed from suspended mode\n");
+ }
+ else {
+ /* modified for adjkerntz */
+ pl = splsoftclock();
+ inittodr(0); /* adjust time to RTC */
+ microtime(&resume_time);
+ getmicrotime(&tmp_time);
+ timevaladd(&tmp_time, &diff_time);
#ifdef FIXME
- /* XXX THIS DOESN'T WORK!!! */
- time = tmp_time;
+ /* XXX THIS DOESN'T WORK!!! */
+ time = tmp_time;
#endif
#ifdef APM_FIXUP_CALLTODO
- /* Calculate the delta time suspended */
- timevalsub(&resume_time, &suspend_time);
- /* Fixup the calltodo list with the delta time. */
- adjust_timeout_calltodo(&resume_time);
+ /* Calculate the delta time suspended */
+ timevalsub(&resume_time, &suspend_time);
+ /* Fixup the calltodo list with the delta time. */
+ adjust_timeout_calltodo(&resume_time);
#endif /* APM_FIXUP_CALLTODOK */
- splx(pl);
+ splx(pl);
#ifndef APM_FIXUP_CALLTODO
- second = resume_time.tv_sec - suspend_time.tv_sec;
+ second = resume_time.tv_sec - suspend_time.tv_sec;
#else /* APM_FIXUP_CALLTODO */
- /*
- * We've already calculated resume_time to be the delta between
- * the suspend and the resume.
- */
- second = resume_time.tv_sec;
+ /*
+ * We've already calculated resume_time to be the delta between
+ * the suspend and the resume.
+ */
+ second = resume_time.tv_sec;
#endif /* APM_FIXUP_CALLTODO */
- hour = second / 3600;
- second %= 3600;
- minute = second / 60;
- second %= 60;
- log(LOG_NOTICE, "resumed from suspended mode (slept %02d:%02d:%02d)\n",
- hour, minute, second);
+ hour = second / 3600;
+ second %= 3600;
+ minute = second / 60;
+ second %= 60;
+ log(LOG_NOTICE, "resumed from suspended mode (slept %02d:%02d:%02d)\n",
+ hour, minute, second);
+ }
return 0;
}
@@ -408,35 +511,52 @@
{
int pl;
- pl = splsoftclock();
- microtime(&diff_time);
- inittodr(0);
- microtime(&suspend_time);
- timevalsub(&diff_time, &suspend_time);
- splx(pl);
+ if (!apm_no_clock_adjust) {
+ pl = splsoftclock();
+ microtime(&diff_time);
+ inittodr(0);
+ microtime(&suspend_time);
+ timevalsub(&diff_time, &suspend_time);
+ splx(pl);
+ }
return 0;
}
+/*
+ * Do not suspend immediately after the system is resumed from
+ * suspended mode
+ */
+
static void apm_processevent(void);
-/*
- * Public interface to the suspend/resume:
- *
- * Execute suspend and resume hook before and after sleep, respectively.
- *
- */
+static u_int apm_standbys = 0;
+static u_int apm_suspends = 0;
+static u_int apm_op_inprog = 0;
-void
-apm_suspend(int state)
+static void
+apm_do_suspend(void)
{
struct apm_softc *sc = &apm_softc;
if (!sc)
return;
+ apm_op_inprog = 0;
+ apm_suspends = 0;
+
+ if (sc->suspending != 0) { /* avoid reentry */
+ /* PCG-505: suspend -> save-to-disk */
+ if (sc->initialized) {
+ if (apm_suspend_system(PMST_SUSPEND) == 0)
+ apm_processevent();
+ }
+ return;
+ }
+ sc->suspending = 1;
+
if (sc->initialized) {
apm_execute_hook(hook[APM_HOOK_SUSPEND]);
- if (apm_suspend_system(state) == 0)
+ if (apm_suspend_system(PMST_SUSPEND) == 0)
apm_processevent();
else
/* Failure, 'resume' the system again */
@@ -444,6 +564,61 @@
}
}
+static void
+apm_do_standby(void)
+{
+ apm_op_inprog = 0;
+ apm_standbys = 0;
+
+ apm_default_suspend(&apm_softc);
+ if (apm_suspend_system(PMST_STANDBY) == 0)
+ apm_processevent();
+}
+
+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);
+}
+
+/*
+ * Public interface to the suspend/resume:
+ *
+ * Execute suspend and resume hook before and after sleep, respectively.
+ *
+ */
+
+void
+apm_suspend(int state)
+{
+ switch (state) {
+ case PMST_SUSPEND:
+ if(apm_suspends)
+ return;
+ apm_suspends++;
+ apm_op_inprog++;
+ apm_lastreq_notify();
+ break;
+ case PMST_STANDBY:
+ if(apm_standbys)
+ return;
+ apm_standbys++;
+ apm_op_inprog++;
+ apm_lastreq_notify();
+ break;
+ default:
+ printf("Unknown Suspend state 0x%x\n", state);
+ break;
+ }
+}
+
void
apm_resume(void)
{
@@ -452,6 +627,8 @@
if (!sc)
return;
+ sc->suspending = 0;
+
if (sc->initialized)
apm_execute_hook(hook[APM_HOOK_RESUME]);
}
@@ -472,7 +649,7 @@
if (apm_int(&eax, &ebx, &ecx, &edx))
return 1;
- aip->ai_infoversion = 1;
+ aip->ai_infoversion = (sc->intversion >= INTVERSION(1, 2)) ? 1 : 0;
aip->ai_acline = (ebx >> 8) & 0xff;
aip->ai_batt_stat = ebx & 0xff;
aip->ai_batt_life = ecx & 0xff;
@@ -487,16 +664,21 @@
else /* Time is in seconds */
aip->ai_batt_time = edx;
- eax = (APM_BIOS << 8) | APM_GETCAPABILITIES;
- ebx = 0;
- ecx = 0;
- edx = 0;
- if (apm_int(&eax, &ebx, &ecx, &edx)) {
- aip->ai_batteries = -1; /* Unknown */
- aip->ai_capabilities = 0xff00; /* Unknown, with no bits set */
+ if (sc->intversion >= INTVERSION(1, 2)) {
+ eax = (APM_BIOS << 8) | APM_GETCAPABILITIES;
+ ebx = 0;
+ ecx = 0;
+ edx = 0;
+ if (apm_int(&eax, &ebx, &ecx, &edx)) {
+ aip->ai_batteries = -1; /* Unknown */
+ aip->ai_capabilities = 0xff00; /* Unknown, with no bits set */
+ } else {
+ aip->ai_batteries = ebx & 0xff;
+ aip->ai_capabilities = ecx & 0xf;
+ }
} else {
- aip->ai_batteries = ebx & 0xff;
- aip->ai_capabilities = ecx & 0xf;
+ aip->ai_batteries = -1; /* Unknown */
+ aip->ai_capabilities = 0xff00; /* Unknown, with no bits set */
}
bzero(aip->ai_spare, sizeof aip->ai_spare);
@@ -564,10 +746,21 @@
{
struct apm_softc *sc = &apm_softc;
+ if (apm_op_inprog)
+ apm_lastreq_notify();
+
apm_processevent();
- if (sc->active == 1)
+
+ if (apm_standbys)
+ apm_do_standby();
+
+ if (apm_suspends)
+ apm_do_suspend();
+
+ if (sc->active == 1) {
/* Run slightly more oftan than 1 Hz */
apm_timeout_ch = timeout(apm_timeout, NULL, hz - 1 );
+ }
}
/* enable APM BIOS */
@@ -741,7 +934,7 @@
apm_suspend(PMST_SUSPEND);
break;
OPMEV_DEBUGMESSAGE(PMEV_CRITSUSPEND);
- apm_suspend(PMST_SUSPEND);
+ apm_do_suspend();
break;
OPMEV_DEBUGMESSAGE(PMEV_NORMRESUME);
apm_resume();
@@ -750,11 +943,17 @@
apm_resume();
break;
OPMEV_DEBUGMESSAGE(PMEV_STANDBYRESUME);
- apm_resume();
+#if 0
+ apm_resume(); /* should we call this or not? */
+#else /* 0 */
+ inittodr(0); /* adjust time to RTC */
+#endif /* 0 */
break;
OPMEV_DEBUGMESSAGE(PMEV_BATTERYLOW);
apm_battery_low();
+#ifdef APM_BATT_LOW_SUSPEND
apm_suspend(PMST_SUSPEND);
+#endif /* APM_BATT_LOW_SUSPEND */
break;
OPMEV_DEBUGMESSAGE(PMEV_POWERSTATECHANGE);
break;
@@ -781,6 +980,27 @@
{
#define APM_KERNBASE KERNBASE
struct apm_softc *sc = &apm_softc;
+ int apm_force_apm10 = 0;
+ int apm_force_64k_segments = 0;
+ int rversion, rmajorversion, rminorversion;
+
+#ifdef FORCE_APM10
+ apm_force_apm10 = 1;
+#else /* FORCE_APM10 */
+ apm_force_apm10 = (dvp->id_flags & APM_FORCE_APM10_FLAG);
+#endif /* FORCE_APM10 */
+
+#ifdef APM_NO_CLOCK_ADJUST
+ apm_no_clock_adjust = 1;
+#else /* APM_NO_CLOCK_ADJUST */
+ apm_no_clock_adjust = (dvp->id_flags & APM_NO_CLOCK_ADJUST_FLAG);
+#endif /* APM_NO_CLOCK_ADJUST */
+
+#ifdef APM_FORCE_64K_SEG
+ apm_force_64k_segments = 1;
+#else /* APM_FORCE_64K_SEG */
+ apm_force_64k_segments = (dvp->id_flags & APM_FORCE_64K_SEG_FLAG);
+#endif /* APM_FORCE_64K_SEG */
sc->initialized = 0;
@@ -791,13 +1011,85 @@
sc->cs16_base = (apm_cs16_base << 4) + APM_KERNBASE;
sc->cs32_base = (apm_cs32_base << 4) + APM_KERNBASE;
sc->ds_base = (apm_ds_base << 4) + APM_KERNBASE;
- sc->cs32_limit = apm_cs32_limit - 1;
- if (apm_cs16_limit == 0)
- apm_cs16_limit == apm_cs32_limit;
- sc->cs16_limit = apm_cs16_limit - 1;
- sc->ds_limit = apm_ds_limit - 1;
sc->cs_entry = apm_cs_entry;
+ /*
+ * Some bogus APM V1.1 BIOSes do not return any
+ * size limits in the registers they are supposed to.
+ * We forced them to zero before calling the BIOS
+ * (see apm_init.S), so if we see zero limits here
+ * we assume that means they should be 64k (and trimmed
+ * if needed for legitimate memory needs).
+ */
+
+ /* for V1.0 and bogus BIOSes */
+ if (apm_version <= 0x100 || apm_version >= 0xa00 || apm_force_apm10) {
+ apm_force_64k_segments = 1;
+ }
+
+ if (apm_force_64k_segments) {
+ sc->cs32_limit = 0xffff;
+ sc->cs16_limit = 0xffff;
+ sc->ds_limit = 0xffff;
+ } else {
+ /* code segment (32 bit) */
+ if (apm_cs32_limit == 0) {
+ /* XXX
+ * some BIOSes are lame, even if v1.1.
+ * (Or maybe they want 64k even though they can
+ * only ask for 64k-1?)
+ */
+#ifdef APM_DEBUG
+ printf("apmattach: lame v%04lx bios gave zero len code32, pegged to 64k\n",
+ apm_version);
+#endif
+ sc->cs32_limit = 0xffff;
+ } else {
+ sc->cs32_limit = apm_cs32_limit - 1;
+ }
+
+ /* code segment (16 bit) */
+ if (apm_cs16_limit == 0) {
+#ifdef APM_DEBUG
+ printf("apmattach: v%04lx bios gave zero len code16, pegged to code32's one\n",
+ apm_version);
+#endif
+ sc->cs16_limit = sc->cs32_limit;
+ } else {
+ sc->cs16_limit = apm_cs16_limit - 1;
+ }
+
+ /* data segment */
+ if (apm_ds_limit == 0) {
+ /* XXX
+ * some BIOSes are lame, even if v1.1.
+ * just assume that means they should be 64k :-)
+ * TODO:
+ * need to confirm that segment in an available
+ * location within ISA hole or at page zero or
+ * above biosbasemem and below ISA hole end.
+ * if there is no avaivable locations, set up
+ * the segment descriptor to just the first byte
+ * of the code segment, read only.
+ */
+#ifdef APM_DEBUG
+ printf("apmattach: lame v%04lx bios gave zero len data, tentative 64k\n",
+ apm_version);
+#endif
+ sc->ds_limit = 0xffff;
+ } else {
+ sc->ds_limit = apm_ds_limit - 1;
+ }
+ }
+
+ if (sc->cs32_limit < sc->cs_entry + 4) {
+#ifdef APM_DEBUG
+ printf("apmattach: nonsensical BIOS code length %d ignored (entry point offset is %d)\n",
+ sc->cs32_limit, sc->cs_entry);
+#endif
+ sc->cs32_limit = 0xffff;
+ }
+
/* Always call HLT in idle loop */
sc->always_halt_cpu = 1;
@@ -807,14 +1099,14 @@
/* print bootstrap messages */
#ifdef APM_DEBUG
- printf("apm: APM BIOS version %04x\n", apm_version);
+ printf("apm: APM BIOS version %04lx\n", apm_version);
printf("apm: Code32 0x%08x, Code16 0x%08x, Data 0x%08x\n",
sc->cs32_base, sc->cs16_base, sc->ds_base);
printf("apm: Code entry 0x%08x, Idling CPU %s, Management %s\n",
sc->cs_entry, is_enabled(sc->slow_idle_cpu),
is_enabled(!sc->disabled));
printf("apm: CS32_limit=0x%x, CS16_limit=0x%x, DS_limit=0x%x\n",
- (u_short)sc->cs32_limit, (u_short)sc->cs16_limit, (u_short)sc->ds_limit);
+ sc->cs32_limit, sc->cs16_limit, sc->ds_limit);
#endif /* APM_DEBUG */
#if 0
@@ -831,35 +1123,60 @@
apm_addr.segment = GSEL(GAPMCODE32_SEL, SEL_KPL);
apm_addr.offset = sc->cs_entry;
- if ((dvp->id_flags & 0x10)) {
- if ((dvp->id_flags & 0xf) >= 0x2) {
+ rversion = apm_version;
+ rminorversion = ((rversion & 0x00f0) >> 4) * 10 +
+ ((rversion & 0x000f) >> 0);
+ rmajorversion = ((rversion & 0xf000) >> 12) * 10 +
+ ((rversion & 0x0f00) >> 8);
+
+ if (apm_force_apm10) {
+ apm_version = 0x100;
+ sc->majorversion = 1;
+ sc->minorversion = 0;
+ sc->intversion = INTVERSION(1, 0);
+ printf("apm: running in APM 1.0 compatible mode\n");
+ }
+ else{
+ if (rmajorversion > 1 ||
+ (rmajorversion == 1 && rminorversion >= 2)) {
apm_driver_version(0x102);
- }
- if (!apm_version && (dvp->id_flags & 0xf) >= 0x1) {
+ sc->intversion = INTVERSION(1, 2);
+ } else if (rmajorversion == 1 && rminorversion >= 1) {
apm_driver_version(0x101);
+ sc->intversion = INTVERSION(1, 1);
}
- } else {
- apm_driver_version(0x102);
- if (!apm_version)
- apm_driver_version(0x101);
- }
- if (!apm_version)
- apm_version = 0x100;
- sc->minorversion = ((apm_version & 0x00f0) >> 4) * 10 +
- ((apm_version & 0x000f) >> 0);
- sc->majorversion = ((apm_version & 0xf000) >> 12) * 10 +
- ((apm_version & 0x0f00) >> 8);
+ if (!apm_version)
+ apm_version = 0x100;
- sc->intversion = INTVERSION(sc->majorversion, sc->minorversion);
+ sc->minorversion = ((apm_version & 0x00f0) >> 4) * 10 +
+ ((apm_version & 0x000f) >> 0);
+ sc->majorversion = ((apm_version & 0xf000) >> 12) * 10 +
+ ((apm_version & 0x0f00) >> 8);
+
+ if ((sc->majorversion == 1 && sc->minorversion == 0
+ && rmajorversion >= 1 && rminorversion >= 1)
+ || sc->majorversion > 10 /* for broken APM 1.1 */
+ ) {
+ apm_version = 0x100;
+ sc->majorversion = 1;
+ sc->minorversion = 0;
+ sc->intversion = INTVERSION(1, 0);
+ printf("apm: running in APM 1.0 compatible mode\n");
+ }
+ else{
+ sc->intversion = INTVERSION(sc->majorversion, sc->minorversion);
#ifdef APM_DEBUG
- if (sc->intversion >= INTVERSION(1, 1))
- printf("apm: Engaged control %s\n", is_enabled(!sc->disengaged));
+ if (sc->intversion >= INTVERSION(1, 1))
+ printf("apm: Engaged control %s\n",
+ is_enabled(!sc->disengaged));
#endif
- printf("apm: found APM BIOS version %d.%d\n",
- sc->majorversion, sc->minorversion);
+ printf("apm: found APM BIOS version %d.%d\n",
+ sc->majorversion, sc->minorversion);
+ }
+ }
#ifdef APM_DEBUG
printf("apm: Slow Idling CPU %s\n", is_enabled(sc->slow_idle_cpu));
@@ -901,6 +1218,8 @@
apm_hook_establish(APM_HOOK_SUSPEND, &sc->sc_suspend);
apm_hook_establish(APM_HOOK_RESUME , &sc->sc_resume);
+ at_shutdown(apm_power_off, NULL, SHUTDOWN_POWER_OFF);
+
apm_event_enable();
sc->initialized = 1;
@@ -939,7 +1258,7 @@
if (minor(dev) != 0 || !sc->initialized)
return (ENXIO);
#ifdef APM_DEBUG
- printf("APM ioctl: cmd = 0x%x\n", cmd);
+ printf("APM ioctl: cmd = 0x%lx\n", cmd);
#endif
switch (cmd) {
case APMIO_SUSPEND:
@@ -991,11 +1310,13 @@
case APMIO_DISPLAY:
newstate = *(int *)addr;
if (apm_display(newstate))
error = ENXIO;
break;
case APMIO_BIOS:
- if (apm_bios_call((struct apm_bios_arg*)addr) == 0)
+ if ((error = apm_do_int((struct apm_bios_arg*)addr)) == 0)
((struct apm_bios_arg*)addr)->eax &= 0xff;
+ else if (error == 1)
+ error = 0;
break;
default:
error = EINVAL;
To Unsubscribe: send mail to majordomo@FreeBSD.org
with "unsubscribe freebsd-mobile" in the body of the message
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?199902141646.BAA21353>
