Skip site navigation (1)Skip section navigation (2)
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>